diff --git a/.reviewboardrc b/.reviewboardrc deleted file mode 100644 index 15ef0b5..0000000 --- a/.reviewboardrc +++ /dev/null @@ -1,2 +0,0 @@ -REVIEWBOARD_URL = 'https://git.reviewboard.kde.org' -REPOSITORY = 'git://anongit.kde.org/kscreen' diff --git a/CMakeLists.txt b/CMakeLists.txt index fcf93c3..cdfb2d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,41 @@ project(KScreen) -set(PROJECT_VERSION "5.8.90") +set(PROJECT_VERSION "5.12.80") cmake_minimum_required(VERSION 2.8.12) set(KSCREEN_VERSION ${PROJECT_VERSION}) add_definitions("-DKSCREEN_VERSION=\"${KSCREEN_VERSION}\"") find_package(ECM 1.6.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) find_package(Qt5 REQUIRED COMPONENTS QuickWidgets Test) -find_package(KF5 REQUIRED COMPONENTS DBusAddons Config ConfigWidgets I18n XmlGui GlobalAccel WidgetsAddons Package Plasma PlasmaQuick Declarative) + +find_package(KF5 REQUIRED COMPONENTS DBusAddons Config ConfigWidgets I18n XmlGui GlobalAccel WidgetsAddons + Declarative IconThemes Plasma PlasmaQuick +) set(MIN_LIBKSCREEN_VERSION "5.2.91") find_package(KF5Screen ${MIN_LIBKSCREEN_VERSION} REQUIRED) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMQtDeclareLoggingCategory) include(ECMInstallIcons) include(ECMMarkAsTest) include(FeatureSummary) - +include(ECMQtDeclareLoggingCategory) add_subdirectory(icons) add_subdirectory(kcm) add_subdirectory(configmodule) #add_subdirectory(plasma) add_subdirectory(kded) add_subdirectory(tests) add_subdirectory(console) +install( FILES kscreen.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) + feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/console/console.cpp b/console/console.cpp index 4388501..8225c0e 100644 --- a/console/console.cpp +++ b/console/console.cpp @@ -1,216 +1,224 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "console.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +static QTextStream cout(stdout); + namespace KScreen { namespace ConfigSerializer { // Exported private symbol in configserializer_p.h in KScreen extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); } } using namespace KScreen; Console::Console(const ConfigPtr &config) : QObject() , m_config(config) { } Console::~Console() { } #include void Console::printConfig() { if (!m_config) { qDebug() << "Config is invalid, probably backend couldn't load"; return; } if (!m_config->screen()) { qDebug() << "No screen in the configuration, broken backend"; return; } connect(m_config.data(), &Config::primaryOutputChanged, [&](const OutputPtr &output) { - qDebug() << "New primary output: " << output->id() << output->name(); + if (output) { + qDebug() << "New primary output: " + << output->id() << output->name(); + } else { + qDebug() << "No primary output."; + } }); qDebug() << "Screen:"; qDebug() << "\tmaxSize:" << m_config->screen()->maxSize(); qDebug() << "\tminSize:" << m_config->screen()->minSize(); qDebug() << "\tcurrentSize:" << m_config->screen()->currentSize(); OutputList outputs = m_config->outputs(); Q_FOREACH(const OutputPtr &output, outputs) { qDebug() << "\n-----------------------------------------------------\n"; qDebug() << "Id: " << output->id(); qDebug() << "Name: " << output->name(); qDebug() << "Type: " << typetoString(output->type()); qDebug() << "Connected: " << output->isConnected(); if (!output->isConnected()) { continue; } qDebug() << "Enabled: " << output->isEnabled(); qDebug() << "Primary: " << output->isPrimary(); qDebug() << "Rotation: " << output->rotation(); qDebug() << "Pos: " << output->pos(); qDebug() << "MMSize: " << output->sizeMm(); if (output->currentMode()) { qDebug() << "Size: " << output->size(); } + qDebug() << "Scale: " << output->scale(); if (output->clones().isEmpty()) { qDebug() << "Clones: " << "None"; } else { qDebug() << "Clones: " << output->clones().count(); } qDebug() << "Mode: " << output->currentModeId(); qDebug() << "Preferred Mode: " << output->preferredModeId(); qDebug() << "Preferred modes: " << output->preferredModes(); qDebug() << "Modes: "; ModeList modes = output->modes(); Q_FOREACH(const ModePtr &mode, modes) { qDebug() << "\t" << mode->id() << " " << mode->name() << " " << mode->size() << " " << mode->refreshRate(); } Edid* edid = output->edid(); qDebug() << "EDID Info: "; if (edid && edid->isValid()) { qDebug() << "\tDevice ID: " << edid->deviceId(); qDebug() << "\tName: " << edid->name(); qDebug() << "\tVendor: " << edid->vendor(); qDebug() << "\tSerial: " << edid->serial(); qDebug() << "\tEISA ID: " << edid->eisaId(); qDebug() << "\tHash: " << edid->hash(); qDebug() << "\tWidth: " << edid->width(); qDebug() << "\tHeight: " << edid->height(); qDebug() << "\tGamma: " << edid->gamma(); qDebug() << "\tRed: " << edid->red(); qDebug() << "\tGreen: " << edid->green(); qDebug() << "\tBlue: " << edid->blue(); qDebug() << "\tWhite: " << edid->white(); } else { qDebug() << "\tUnavailable"; } } } QString Console::typetoString(const Output::Type& type) const { switch (type) { case Output::Unknown: return QStringLiteral("Unknown"); case Output::Panel: return QStringLiteral("Panel (Laptop)"); case Output::VGA: return QStringLiteral("VGA"); case Output::DVI: return QStringLiteral("DVI"); case Output::DVII: return QStringLiteral("DVI-I"); case Output::DVIA: return QStringLiteral("DVI-A"); case Output::DVID: return QStringLiteral("DVI-D"); case Output::HDMI: return QStringLiteral("HDMI"); case Output::TV: return QStringLiteral("TV"); case Output::TVComposite: return QStringLiteral("TV-Composite"); case Output::TVSVideo: return QStringLiteral("TV-SVideo"); case Output::TVComponent: return QStringLiteral("TV-Component"); case Output::TVSCART: return QStringLiteral("TV-SCART"); case Output::TVC4: return QStringLiteral("TV-C4"); case Output::DisplayPort: return QStringLiteral("DisplayPort"); }; return QStringLiteral("Invalid Type") + QString::number(type); } void Console::printJSONConfig() { QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); - qDebug() << doc.toJson(QJsonDocument::Indented); + cout << doc.toJson(QJsonDocument::Indented); } void Console::printSerializations() { QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kscreen/"; qDebug() << "Configs in: " << path; QDir dir(path); QStringList files = dir.entryList(QDir::Files); qDebug() << "Number of files: " << files.count() << endl; QJsonDocument parser; Q_FOREACH(const QString fileName, files) { QJsonParseError error; qDebug() << fileName; QFile file(path + "/" + fileName); file.open(QFile::ReadOnly); QVariant data = parser.fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError) { qDebug() << " " << "can't parse file"; qDebug() << " " << error.errorString(); continue; } qDebug() << parser.toJson(QJsonDocument::Indented) << endl; } } void Console::monitor() { ConfigMonitor::instance()->addConfig(m_config); } void Console::monitorAndPrint() { monitor(); connect(ConfigMonitor::instance(), &ConfigMonitor::configurationChanged, this, &Console::printConfig); } diff --git a/console/main.cpp b/console/main.cpp index 9e5d05f..fe5d734 100644 --- a/console/main.cpp +++ b/console/main.cpp @@ -1,127 +1,127 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include #include #include #include #include #include #include #include #include #include "console.h" using namespace std; void configReceived(KScreen::ConfigOperation *op) { const KScreen::ConfigPtr config = qobject_cast(op)->config(); const QString command = op->property("command").toString(); const qint64 msecs = QDateTime::currentMSecsSinceEpoch() - op->property("start").toLongLong(); qDebug() << "Received config. Took" << msecs << "milliseconds"; Console *console = new Console(config); if (command.isEmpty()) { console->printConfig(); console->monitorAndPrint(); } else if (command == QLatin1String("monitor")) { QTextStream(stdout) << "Remember to enable KSRandR or KSRandR11 in kdebugdialog" << endl; //Print config so that we have some pivot data console->printConfig(); console->monitor(); //Do nothing, enable backend output to see debug } else if (command == QLatin1String("outputs")) { console->printConfig(); qApp->quit(); } else if (command == QLatin1String("config")) { console->printSerializations(); qApp->quit(); } else if (command == QLatin1String("bug")) { QTextStream(stdout) << QStringLiteral("\n========================xrandr --verbose==========================\n"); QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(QStringLiteral("xrandr"), QStringList(QStringLiteral("--verbose"))); proc.waitForFinished(); QTextStream(stdout) << proc.readAll().data(); QTextStream(stdout) << QStringLiteral("\n========================Outputs===================================\n"); console->printConfig(); QTextStream(stdout) << QStringLiteral("\n========================Configurations============================\n"); console->printSerializations(); qApp->quit(); } else if (command == QLatin1String("json")) { console->printJSONConfig(); qApp->quit(); } else { qApp->quit(); } } int main (int argc, char *argv[]) { dup2(1, 2); QGuiApplication app(argc, argv); KAboutData aboutData(QStringLiteral("kscreen-console"), i18n("KScreen Console"), QStringLiteral("1.0"), i18n("KScreen Console"), KAboutLicense::GPL, i18n("(c) 2012 KScreen Team")); KAboutData::setApplicationData(aboutData); aboutData.addAuthor(i18n("Alejandro Fiestas Olivares"), i18n("Maintainer"), QStringLiteral("afiestas@kde.org"), QStringLiteral("http://www.afiestas.org/")); QCommandLineParser parser; parser.setApplicationDescription( i18n("KScreen Console is a CLI tool to query KScreen status\n\n" "Commands:\n" " bug Show information needed for a bug report\n" " config Show KScreen config files\n" - " outputs Show outout information\n" + " outputs Show output information\n" " monitor Monitor for changes\n" " json Show current KScreen config")); parser.addHelpOption(); parser.addPositionalArgument(QStringLiteral("command"), i18n("Command to execute"), QStringLiteral("bug|config|outputs|monitor|json")); parser.addPositionalArgument(QStringLiteral("[args...]"), i18n("Arguments for the specified command")); parser.process(app); QString command; if (!parser.positionalArguments().isEmpty()) { command = parser.positionalArguments().first(); } qDebug() << "START: Requesting Config"; KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation(); op->setProperty("command", command); op->setProperty("start", QDateTime::currentMSecsSinceEpoch()); QObject::connect(op, &KScreen::GetConfigOperation::finished, [&](KScreen::ConfigOperation *op) { configReceived(op); }); app.exec(); } diff --git a/icons/hi48-actions-kdocumentinfo.png b/icons/48-actions-kdocumentinfo.png similarity index 100% rename from icons/hi48-actions-kdocumentinfo.png rename to icons/48-actions-kdocumentinfo.png diff --git a/icons/CMakeLists.txt b/icons/CMakeLists.txt index 832a8e5..56eb325 100644 --- a/icons/CMakeLists.txt +++ b/icons/CMakeLists.txt @@ -1 +1,9 @@ -ecm_install_icons( ${ICON_INSTALL_DIR} ) +ecm_install_icons( + ICONS + 48-actions-kdocumentinfo.png + sc-actions-kdocumentinfo.svgz + DESTINATION + ${KDE_INSTALL_ICONDIR} + THEME + hicolor +) diff --git a/icons/hisc-actions-kdocumentinfo.svgz b/icons/sc-actions-kdocumentinfo.svgz similarity index 100% rename from icons/hisc-actions-kdocumentinfo.svgz rename to icons/sc-actions-kdocumentinfo.svgz diff --git a/kcm/kcm_kscreen.desktop.cmake b/kcm/kcm_kscreen.desktop.cmake index 8ec697c..ad26004 100644 --- a/kcm/kcm_kscreen.desktop.cmake +++ b/kcm/kcm_kscreen.desktop.cmake @@ -1,106 +1,132 @@ [Desktop Entry] Exec=kcmshell5 kcm_kscreen Icon=preferences-desktop-display-randr Type=Service X-KDE-ServiceTypes=KCModule X-KDE-Library=kcm_kscreen X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=display X-KDE-Weight=40 X-KDE-PluginInfo-Version=@KSCREEN_VERSION@ Name=Displays Name[ca]=Pantalles Name[ca@valencia]=Pantalles +Name[cs]=Obrazovky Name[da]=Skærme Name[de]=Anzeigen Name[el]=Οθόνες Name[en_GB]=Displays Name[es]=Pantallas +Name[et]=Monitorid +Name[eu]=Bistaratzaileak Name[fi]=Näytöt +Name[fr]=Affichages +Name[gl]=Pantallas Name[he]=תצוגות +Name[hu]=Megjelenítők +Name[ia]=Monstratores +Name[id]=Displays Name[it]=Schermi +Name[ko]=디스플레이 +Name[lt]=Ekranai Name[nl]=Schermen Name[nn]=Skjermar Name[pl]=Ekrany Name[pt]=Ecrãs +Name[pt_BR]=Telas Name[ru]=Экраны Name[sk]=Obrazovky Name[sl]=Zasloni +Name[sr]=Екрани +Name[sr@ijekavian]=Екрани +Name[sr@ijekavianlatin]=Ekrani +Name[sr@latin]=Ekrani Name[sv]=Bildskärmar +Name[tr]=Ekranlar Name[uk]=Дисплеї Name[x-test]=xxDisplaysxx Name[zh_CN]=显示 +Name[zh_TW]=顯示 Comment=Manage and configure monitors and displays Comment[ar]=أدر واضبط الشّاشات والعروض -Comment[ast]=Xestiona y configura los monitores y pantalles Comment[bs]=Upravljanje i konfiguracija monitora i ekrana Comment[ca]=Gestiona i configura els monitors i pantalles Comment[ca@valencia]=Gestiona i configura els monitors i pantalles Comment[cs]=Spravovat a nastavit monitory a zobrazení Comment[da]=Håndtér og indstil monitorer og skærme Comment[de]=Verwaltung und Einrichtung vom Monitoren und Anzeigen Comment[el]=Διαχείριση και διαμόρφωση οθονών και απεικονίσεων Comment[en_GB]=Manage and configure monitors and displays Comment[es]=Gestionar y configurar monitores y pantallas Comment[et]=Monitoride ja ekraanide haldamine ja seadistamine +Comment[eu]=Kudeatu eta konfiguratu monitoreak eta bistaratzaileak Comment[fi]=Näyttöjen asetusten hallinta Comment[fr]=Gère et configure les moniteurs et les écrans Comment[gl]=Xestiona a configura os monitores e a resolución Comment[he]=נהל את הגדרות המסכים והתצוגות שלך Comment[hu]=Monitorok és kijelzők kezelése és beállítása +Comment[ia]=Gere e configura monitors e monstratores +Comment[id]=Kelola dan konfigurasi monitor dan display Comment[it]=Gestisci e configura monitor e schermi Comment[ko]=모니터와 디스플레이 설정 및 관리 Comment[lt]=Tvarkyti ir konfigūruoti monitorius ir ekranus Comment[nb]=Håndter og sett opp monitorer og skjermer Comment[nl]=Monitoren en schermen beheren en instellen Comment[nn]=Set opp skjermar og skjermbilete Comment[pa]=ਮਾਨੀਟਰ ਤੇ ਡਿਸਪਲੇਅ ਦਾ ਪਰਬੰਧ ਤੇ ਸੰਰਚਨਾ ਕਰੋ Comment[pl]=Zarządzanie i ustawienia ekranów Comment[pt]=Faz a gestão e configuração dos monitores e ecrãs Comment[pt_BR]=Gerencia e configura monitores e telas Comment[ro]=Gestionează și configurează monitoare și afișaje Comment[ru]=Настройка экранов и видеовыходов Comment[sk]=Správa a nastavenie monitorov a obrazoviek Comment[sl]=Upravljajte in nastavljajte zaslone in prikazovalnike Comment[sr]=Управљање и подешавање монитора̂ и екрана̂ Comment[sr@ijekavian]=Управљање и подешавање монитора̂ и екрана̂ Comment[sr@ijekavianlatin]=Upravljanje i podešavanje monitorâ̂ i ekranâ̂ Comment[sr@latin]=Upravljanje i podešavanje monitorâ̂ i ekranâ̂ Comment[sv]=Hantera och ställ in bildskärmar Comment[tr]=Ekranları ve görüntüleri yönet, yapılandır Comment[uk]=Керування і налаштовування моніторів і дисплеїв Comment[x-test]=xxManage and configure monitors and displaysxx Comment[zh_CN]=管理和配置显示和监视器 Comment[zh_TW]=管理與設定螢幕與顯示 X-KDE-Keywords=display,monitor,scale,scaling,resolution,orientation,outputs -X-KDE-Keywords[ast]=pantalla,pantalles,monitores,monitor,escalar,escala,escaláu,resolución,orientación,salíes,salida X-KDE-Keywords[ca]=pantalla,monitor,escala,escalat,resolució,orientació,sortides X-KDE-Keywords[ca@valencia]=pantalla,monitor,escala,escalat,resolució,orientació,eixides X-KDE-Keywords[da]=skærm,monitor,skaler,opløsning,orientering,output X-KDE-Keywords[de]=Anzeige,Monitor,Bildschirm,Skalierung,Auflösung,Ausrichtung,Ausgabe X-KDE-Keywords[el]=απεικόνιση,οθόνη,κλίμακα,κλιμάκωση,ανάλυση,προσανατολισμός,έξοδοι X-KDE-Keywords[en_GB]=display,monitor,scale,scaling,resolution,orientation,outputs X-KDE-Keywords[es]=pantalla,monitor,escala,escalado,resolución,orientación,salidas X-KDE-Keywords[et]=kuva,monitor,ekraan,skaleerimine,lahutusvõime,eraldusvõime,orientatsioon,väljundid +X-KDE-Keywords[eu]=bistaratzaile,monitore,eskalatu,eskalatze,bereizmen,orientazioa,irteerak X-KDE-Keywords[fi]=display,monitor,scale,scaling,resolution,orientation,outputs,näyttö,monitori,skaalaus,resoluutio,tarkkuus,suunta,kierto,ulostulot X-KDE-Keywords[fr]=affichage,moniteur,échelle,mise à l'échelle,résolution,orientation,sorties -X-KDE-Keywords[gl]=display,pantalla,monitor,scale,escala,scaling,escalar,resolution,resolución,orientation,orientación,outputs,saída,saídas +X-KDE-Keywords[gl]=display,pantalla,monitor,scale,cambiar de escala,scaling,cambio de escala,resolution,resolución,orientation,orientación,outputs,saída,saídas +X-KDE-Keywords[he]=display,monitor,scale,scaling,resolution,orientation,outputs,תצוגה,מוניטור,רצולוציה,כיוון +X-KDE-Keywords[hu]=megjelenítés,monitor,skála,skálázás,felbontás,tájolás,kimenetek +X-KDE-Keywords[ia]=monstrator, monitor, scala, scalar, resolution, orientation, exitos +X-KDE-Keywords[id]=display,monitor,scale,scaling,resolution,orientation,outputs X-KDE-Keywords[it]=schermo,monitor,scala,riscalatura,risoluzione,orientazione,uscite X-KDE-Keywords[ko]=display,monitor,scale,scaling,resolution,orientation,outputs,디스플레이,모니터,비율,배율,해상도,출력,방향 +X-KDE-Keywords[lt]=ekranas,vaizduoklis,monitorius,mastelis,skiriamoji geba,padėtis,orientacija,išvedimas X-KDE-Keywords[nl]=scherm,monitor,schaal,schalen,resolutie,oriëntatie,uitvoer X-KDE-Keywords[nn]=skjermbilete,skjerm,skala,skalering,oppløysing,skjermoppløysing,retning,utgang X-KDE-Keywords[pl]=wyświetlacz,monitor,skala,skalowanie,rozdzielczość,kierunek,orientacja,wyjścia X-KDE-Keywords[pt]=ecrã,monitor,escala,resolução,orientação,saídas X-KDE-Keywords[pt_BR]=tela,monitor,escala,ajuste,resolução,orientação,saídas X-KDE-Keywords[ru]=display,monitor,scale,scaling,resolution,orientation,outputs,экран,монитор,масштаб,разрешение,ориентация,выводы X-KDE-Keywords[sk]=displej,monitor,mierka,škálovanie,rozlíšenie,orientácia,výstupy X-KDE-Keywords[sl]=prikaz,zaslon,monitor,umerjanje,merilo,ločljivost,resolucija,orientacija,usmerjenost,izhodi X-KDE-Keywords[sr]=display,monitor,scale,scaling,resolution,oritentation,outputs,приказ,монитор,екран,скалирање,резолуција,оријентација,излаз X-KDE-Keywords[sr@ijekavian]=display,monitor,scale,scaling,resolution,oritentation,outputs,приказ,монитор,екран,скалирање,резолуција,оријентација,излаз X-KDE-Keywords[sr@ijekavianlatin]=display,monitor,scale,scaling,resolution,oritentation,outputs,prikaz,monitor,ekran,skaliranje,rezolucija,orijentacija,izlaz X-KDE-Keywords[sr@latin]=display,monitor,scale,scaling,resolution,oritentation,outputs,prikaz,monitor,ekran,skaliranje,rezolucija,orijentacija,izlaz X-KDE-Keywords[sv]=skärm,bildskärm,skala,skalning,upplösning,orientering,utgångar +X-KDE-Keywords[tr]=ekran,monitör,ölçek,ölçeklendirme,çözünürlük,yönelim,çıktılar X-KDE-Keywords[uk]=display,monitor,scale,scaling,resolution,orientation,outputs,дисплей,монітор,масштаб,масштабування,роздільність,здатність,орієнтація,виведення,виходи X-KDE-Keywords[x-test]=xxdisplayxx,xxmonitorxx,xxscalexx,xxscalingxx,xxresolutionxx,xxorientationxx,xxoutputsxx X-KDE-Keywords[zh_CN]=display,monitor,scale,scaling,resolution,orientation,outputs,显示,监视器,缩放,分辨率,旋转,方位,输出 +X-KDE-Keywords[zh_TW]=display,monitor,scale,scaling,resolution,orientation,outputs diff --git a/kcm/qml/Output.qml b/kcm/qml/Output.qml index b21f441..3699465 100644 --- a/kcm/qml/Output.qml +++ b/kcm/qml/Output.qml @@ -1,308 +1,308 @@ /* Copyright (C) 2012 Dan Vratil 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 */ import QtQuick 2.1 import QtGraphicalEffects 1.0 import org.kde.kscreen 1.0 QMLOutput { id: root; signal clicked(); signal primaryTriggered(string self); signal enabledToggled(string self); signal mousePressed(); signal mouseReleased(); property bool isDragged: monitorMouseArea.drag.active; property bool isDragEnabled: true; property bool isToggleButtonVisible: false; property bool hasMoved: false; width: monitorMouseArea.width; height: monitorMouseArea.height; visible: (opacity > 0); opacity: output.connected ? 1.0 : 0.0; Component.onCompleted: { root.updateRootProperties(); } SystemPalette { id: palette; } MouseArea { id: monitorMouseArea; width: root.currentOutputWidth * screen.outputScale; height: root.currentOutputHeight * screen.outputScale anchors.centerIn: parent; opacity: root.output.enabled ? 1.0 : 0.5; transformOrigin: Item.Center; rotation: { if (output.rotation == KScreenOutput.None) { - return 0 + return 0; } else if (output.rotation == KScreenOutput.Left) { - return 90 + return 270; } else if (output.rotation == KScreenOutput.Inverted) { return 180; } else { - return 270; + return 90; } } hoverEnabled: true; preventStealing: true; drag { target: root.isDragEnabled && !root.isCloneMode ? root : null; axis: Drag.XandYAxis; minimumX: 0; maximumX: screen.maxScreenSize.width - root.width; minimumY: 0; maximumY: screen.maxScreenSize.height - root.height; filterChildren: false; } drag.onActiveChanged: { /* If the drag is shorter then the animation then make sure * we won't end up in an inconsistent state */ if (dragActiveChangedAnimation.running) { dragActiveChangedAnimation.complete(); } dragActiveChangedAnimation.running = true; } onPressed: root.clicked(); /* FIXME: This could be in 'Behavior', but MouseArea had * some complaints...to tired to investigate */ PropertyAnimation { id: dragActiveChangedAnimation; target: monitor; property: "opacity"; from: monitorMouseArea.drag.active ? 0.7 : 1.0 to: monitorMouseArea.drag.active ? 1.0 : 0.7 duration: 100; easing.type: "OutCubic"; } Behavior on opacity { PropertyAnimation { property: "opacity"; easing.type: "OutCubic"; duration: 250; } } Behavior on rotation { RotationAnimation { easing.type: "OutCubic" duration: 250; direction: RotationAnimation.Shortest; } } Behavior on width { PropertyAnimation { property: "width"; easing.type: "OutCubic"; duration: 150; } } Behavior on height { PropertyAnimation { property: "height"; easing.type: "OutCubic"; duration: 150; } } Rectangle { id: monitor; anchors.fill: parent; radius: 4; color: palette.window; smooth: true; clip: true; border { color: root.focus ? palette.highlight : palette.shadow; width: 1; Behavior on color { PropertyAnimation { duration: 150; } } } Rectangle { id: posLabel; y: 4; x: 4; width: childrenRect.width + 5; height: childrenRect.height + 2; radius: 4; opacity: root.output.enabled && monitorMouseArea.drag.active ? 1.0 : 0.0; visible: opacity != 0.0; color: "#101010"; Text { id: posLabelText; text: root.outputX + "," + root.outputY; color: "white"; y: 2; x: 2; } } Item { y: ((parent.height - orientationPanel.height) / 2) - (implicitHeight / 2) anchors { left: parent.left; right: parent.right; leftMargin: 5; rightMargin: 5; } Text { id: nameLabel text: if (root.isCloneMode === true) { return ""; } else if (root.output.type != KScreenOutput.Panel && root.output.edid && root.output.edid.name) { return root.output.edid.name; } else { return ""; } color: palette.text; font.pixelSize: 10; anchors { horizontalCenter: parent.horizontalCenter; bottom: labelVendor.top; topMargin: 5; } } Text { id: labelVendor; text: if (root.isCloneMode) { return i18n("Unified Outputs"); } else if (root.output.type == KScreenOutput.Panel) { return i18n("Laptop Screen"); } else if (root.output.edid && root.output.edid.vendor) { return root.output.edid.vendor; } else { return root.output.name; } anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; color: palette.text; font.pixelSize: 14; elide: Text.ElideRight; } Text { id: label text: (labelVendor.text === root.output.name) ? "" : root.output.name color: palette.text; font.pixelSize: 10; anchors { horizontalCenter: parent.horizontalCenter; top: labelVendor.bottom; topMargin: 5; } } } } Item { id: orientationPanelContainer; anchors.fill: monitor; visible: false Rectangle { id: orientationPanel; anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } height: 10; color: root.focus ? palette.highlight : palette.shadow; smooth: true; Behavior on color { PropertyAnimation { duration: 150; } } } } OpacityMask { anchors.fill: orientationPanelContainer; source: orientationPanelContainer; maskSource: monitor; } } Behavior on opacity { PropertyAnimation { duration: 200; easing.type: "OutCubic"; } } } diff --git a/kcm/qml/main.qml b/kcm/qml/main.qml index 971601f..7ca8aa8 100644 --- a/kcm/qml/main.qml +++ b/kcm/qml/main.qml @@ -1,125 +1,123 @@ /* Copyright (C) 2012 Dan Vratil 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 */ import QtQuick 2.1 import QtQuick.Controls 1.1 as Controls import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kquickcontrols 2.0 import org.kde.kscreen 1.0 Item { id: root; property variant virtualScreen: null; objectName: "root"; focus: true; - anchors.fill: parent; - SystemPalette { id: palette; } Rectangle { id: background; anchors.fill: parent; focus: true; color: palette.base; FocusScope { id: outputViewFocusScope; anchors.fill: parent; focus: true; QMLScreen { id: outputView; anchors.fill: parent; clip: true; objectName: "outputView"; } } Column { anchors { left: parent.left; right: identifyButton.left; bottom: parent.bottom; margins: 5; } spacing: 5; Tip { id: dragTip; icon: "dialog-information"; text: i18nd("kcm_displayconfiguration", "Tip: Hold Ctrl while dragging a display to disable snapping"); } Tip { id: noActiveOutputsWarning; icon: "dialog-warning"; text: i18nd("kcm_displayconfiguration", "Warning: There are no active outputs!"); } Tip { id: tooManyActiveOutputs; objectName: "tooManyActiveOutputs"; icon: "dialog-error"; text: i18ndp("kcm_displayconfiguration", "Your system only supports up to %1 active screen", "Your system only supports up to %1 active screens", virtualScreen ? virtualScreen.maxActiveOutputsCount : 1); } } Controls.ToolButton { id: identifyButton objectName: "identifyButton"; anchors { right: parent.right bottom: parent.bottom margins: 5 } height: width width: theme.largeIconSize; iconName: "kdocumentinfo" tooltip: i18nd("kcm_displayconfiguration", "Identify outputs"); } } } diff --git a/kcm/src/CMakeLists.txt b/kcm/src/CMakeLists.txt index ff62a9f..eba7d32 100644 --- a/kcm/src/CMakeLists.txt +++ b/kcm/src/CMakeLists.txt @@ -1,49 +1,52 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcm_displayconfiguration\") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/declarative") + set(kcm_kscreen_SRCS declarative/qmloutput.cpp declarative/qmloutputcomponent.cpp declarative/qmlscreen.cpp collapsablebutton.cpp controlpanel.cpp - debug.cpp outputconfig.cpp primaryoutputcombo.cpp unifiedoutputconfig.cpp resolutionslider.cpp utils.cpp widget.cpp previewwidget.cpp scalingconfig.cpp ) +ecm_qt_declare_logging_category(kcm_kscreen_SRCS HEADER kcm_screen_debug.h IDENTIFIER KSCREEN_KCM CATEGORY_NAME kscreen.kcm) + + ki18n_wrap_ui(kcm_kscreen_SRCS stylepreview.ui scaling.ui) add_library(kcm_kscreen MODULE kcm_kscreen.cpp ${kcm_kscreen_SRCS}) target_link_libraries(kcm_kscreen Qt5::QuickWidgets Qt5::Widgets KF5::Screen KF5::I18n KF5::ConfigCore KF5::ConfigWidgets KF5::WidgetsAddons ) install(TARGETS kcm_kscreen DESTINATION ${PLUGIN_INSTALL_DIR} ) add_executable(kcm_testapp kcm_testapp.cpp ${kcm_kscreen_SRCS}) set_target_properties(kcm_testapp PROPERTIES COMPILE_FLAGS "-DQT_DECLARATIVE_DEBUG") target_link_libraries(kcm_testapp Qt5::QuickWidgets Qt5::Widgets KF5::CoreAddons KF5::I18n KF5::ConfigCore KF5::Screen KF5::WidgetsAddons ) diff --git a/kcm/src/collapsablebutton.cpp b/kcm/src/collapsablebutton.cpp index 6be807e..f4a3b2b 100644 --- a/kcm/src/collapsablebutton.cpp +++ b/kcm/src/collapsablebutton.cpp @@ -1,116 +1,116 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "collapsablebutton.h" #include #include #include #include #include CollapsableButton::CollapsableButton(const QString &text, QWidget *parent) : QWidget(parent) , mCollapsed(false) - , mWidget(0) + , mWidget(nullptr) { QHBoxLayout *layout = new QHBoxLayout(this); mLabel = new QLabel(text, this); layout->addWidget(mLabel); QFont f = mLabel->font(); f.setBold(true); mLabel->setFont(f); mLabel->setIndent(20); } CollapsableButton::~CollapsableButton() { } void CollapsableButton::mouseReleaseEvent(QMouseEvent *ev) { if (ev->button() == Qt::LeftButton) { toggle(); Q_EMIT toggled(); } QWidget::mouseReleaseEvent(ev); } void CollapsableButton::paintEvent(QPaintEvent *ev) { QPainter painter(this); QStyleOption opt; const int h = 20; opt.rect = QRect(0, (height() - h) / 2, h, h); opt.palette = palette(); QStyle::PrimitiveElement pe = mCollapsed ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowDown; style()->drawPrimitive(pe, &opt, &painter); painter.end(); QWidget::paintEvent(ev); } bool CollapsableButton::isCollapsed() const { return mCollapsed; } void CollapsableButton::setCollapsed(bool collapsed) { if (mCollapsed == collapsed) { return; } mCollapsed = collapsed; if (mWidget) { mWidget->setHidden(collapsed); } update(); } QLabel* CollapsableButton::label() const { return mLabel; } void CollapsableButton::setWidget(QWidget *widget) { mWidget = widget; if (mWidget) { mWidget->setHidden(isCollapsed()); } } QWidget *CollapsableButton::widget() const { return mWidget; } void CollapsableButton::toggle() { setCollapsed(!isCollapsed()); Q_EMIT toggled(); } diff --git a/kcm/src/collapsablebutton.h b/kcm/src/collapsablebutton.h index ec73a5e..a269c3e 100644 --- a/kcm/src/collapsablebutton.h +++ b/kcm/src/collapsablebutton.h @@ -1,59 +1,59 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef COLLAPSABLE_BUTTON_H #define COLLAPSABLE_BUTTON_H #include class CollapsableButton : public QWidget { Q_OBJECT public: - explicit CollapsableButton(const QString &text, QWidget *parent = 0); + explicit CollapsableButton(const QString &text, QWidget *parent = nullptr); virtual ~CollapsableButton(); void setCollapsed(bool collapsed); bool isCollapsed() const; void setWidget(QWidget *widget); QWidget* widget() const; QLabel* label() const; public Q_SLOTS: void toggle(); Q_SIGNALS: void toggled(); protected: - virtual void paintEvent(QPaintEvent *ev); - virtual void mouseReleaseEvent(QMouseEvent *ev); + void paintEvent(QPaintEvent *ev) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; private: bool mCollapsed; QLabel *mLabel; QWidget *mWidget; }; #endif // COLLAPSABLE_BUTTON_H diff --git a/kcm/src/controlpanel.cpp b/kcm/src/controlpanel.cpp index a313576..d176383 100644 --- a/kcm/src/controlpanel.cpp +++ b/kcm/src/controlpanel.cpp @@ -1,123 +1,125 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "controlpanel.h" #include "outputconfig.h" #include "unifiedoutputconfig.h" -#include "debug.h" +#include "kcm_screen_debug.h" #include #include ControlPanel::ControlPanel(QWidget *parent) : QFrame(parent) - , mUnifiedOutputCfg(0) + , mUnifiedOutputCfg(nullptr) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); mLayout = new QVBoxLayout(this); } ControlPanel::~ControlPanel() { } void ControlPanel::setConfig(const KScreen::ConfigPtr &config) { qDeleteAll(mOutputConfigs); mOutputConfigs.clear(); delete mUnifiedOutputCfg; - mUnifiedOutputCfg = 0; + mUnifiedOutputCfg = nullptr; if (mConfig) { mConfig->disconnect(this); } mConfig = config; connect(mConfig.data(), &KScreen::Config::outputAdded, this, &ControlPanel::addOutput); connect(mConfig.data(), &KScreen::Config::outputRemoved, this, &ControlPanel::removeOutput); for (const KScreen::OutputPtr &output : mConfig->outputs()) { addOutput(output); } } void ControlPanel::addOutput(const KScreen::OutputPtr &output) { - OutputConfig *outputCfg = new OutputConfig(output, this); + OutputConfig *outputCfg = new OutputConfig(this); outputCfg->setVisible(false); + outputCfg->setShowScaleOption(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); + outputCfg->setOutput(output); connect(outputCfg, &OutputConfig::changed, this, &ControlPanel::changed); mLayout->addWidget(outputCfg); mOutputConfigs << outputCfg; } void ControlPanel::removeOutput(int outputId) { for (OutputConfig *outputCfg : mOutputConfigs) { if (outputCfg->output()->id() == outputId) { mOutputConfigs.removeOne(outputCfg); delete outputCfg; return; } } } void ControlPanel::activateOutput(const KScreen::OutputPtr &output) { // Ignore activateOutput when in unified mode if (mUnifiedOutputCfg) { return; } qCDebug(KSCREEN_KCM) << "Activate output" << output->id(); Q_FOREACH (OutputConfig *cfg, mOutputConfigs) { cfg->setVisible(cfg->output()->id() == output->id()); } } void ControlPanel::setUnifiedOutput(const KScreen::OutputPtr &output) { Q_FOREACH (OutputConfig *config, mOutputConfigs) { if (!config->output()->isConnected()) { continue; } - config->setVisible(output == 0); + config->setVisible(output == nullptr); } if (output.isNull()) { mUnifiedOutputCfg->deleteLater(); - mUnifiedOutputCfg = 0; + mUnifiedOutputCfg = nullptr; } else { mUnifiedOutputCfg = new UnifiedOutputConfig(mConfig, this); mUnifiedOutputCfg->setOutput(output); mUnifiedOutputCfg->setVisible(true); mLayout->insertWidget(mLayout->count() - 2, mUnifiedOutputCfg); connect(mUnifiedOutputCfg, &UnifiedOutputConfig::changed, this, &ControlPanel::changed); } } diff --git a/kcm/src/controlpanel.h b/kcm/src/controlpanel.h index ec18646..7bab7e4 100644 --- a/kcm/src/controlpanel.h +++ b/kcm/src/controlpanel.h @@ -1,68 +1,68 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CONTROLPANEL_H #define CONTROLPANEL_H #include #include class QVBoxLayout; class OutputConfig; class UnifiedOutputConfig; class QLabel; class QCheckBox; class QSlider; class QComboBox; class ControlPanel : public QFrame { Q_OBJECT public: - explicit ControlPanel(QWidget *parent = 0); + explicit ControlPanel(QWidget *parent = nullptr); virtual ~ControlPanel(); void setConfig(const KScreen::ConfigPtr &config); void setUnifiedOutput(const KScreen::OutputPtr &output); public Q_SLOTS: void activateOutput(const KScreen::OutputPtr &output); Q_SIGNALS: void changed(); private Q_SLOTS: void addOutput(const KScreen::OutputPtr &output); void removeOutput(int outputId); private: KScreen::ConfigPtr mConfig; QList mOutputConfigs; QVBoxLayout *mLayout; UnifiedOutputConfig *mUnifiedOutputCfg; }; #endif // CONTROLPANEL_H diff --git a/kcm/src/debug.cpp b/kcm/src/debug.cpp deleted file mode 100644 index de3b5e2..0000000 --- a/kcm/src/debug.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 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 Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "debug.h" - -Q_LOGGING_CATEGORY(KSCREEN_KCM, "kscreen.kcm") diff --git a/kcm/src/debug.h b/kcm/src/debug.h deleted file mode 100644 index 26810ee..0000000 --- a/kcm/src/debug.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 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 Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef KSCREEN_KCM_DEBUG_H -#define KSCREEN_KCM_DEBUG_H - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(KSCREEN_KCM) - -#endif diff --git a/kcm/src/declarative/qmloutput.cpp b/kcm/src/declarative/qmloutput.cpp index e973247..fbda695 100644 --- a/kcm/src/declarative/qmloutput.cpp +++ b/kcm/src/declarative/qmloutput.cpp @@ -1,585 +1,594 @@ /* Copyright (C) 2012 Dan Vratil 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 "qmloutput.h" #include "qmlscreen.h" #include #include #include #include #include const static int sMargin = 0; const static int sSnapArea = 20; const static int sSnapAlignArea = 6; Q_DECLARE_METATYPE(KScreen::ModePtr) bool operator>(const QSize &sizeA, const QSize &sizeB) { return ((sizeA.width() > sizeB.width()) && (sizeA.height() > sizeB.height())); } QMLOutput::QMLOutput(QQuickItem *parent): QQuickItem(parent), - m_screen(0), - m_cloneOf(0), - m_leftDock(0), - m_topDock(0), - m_rightDock(0), - m_bottomDock(0), + m_screen(nullptr), + m_cloneOf(nullptr), + m_leftDock(nullptr), + m_topDock(nullptr), + m_rightDock(nullptr), + m_bottomDock(nullptr), m_isCloneMode(false) { connect(this, &QMLOutput::xChanged, this, static_cast(&QMLOutput::moved)); connect(this, &QMLOutput::yChanged, this, static_cast(&QMLOutput::moved)); } QMLOutput::~QMLOutput() { } KScreen::Output* QMLOutput::output() const { return m_output.data(); } KScreen::OutputPtr QMLOutput::outputPtr() const { return m_output; } void QMLOutput::setOutputPtr(const KScreen::OutputPtr &output) { Q_ASSERT(m_output.isNull()); m_output = output; Q_EMIT outputChanged(); connect(m_output.data(), &KScreen::Output::rotationChanged, this, &QMLOutput::updateRootProperties); connect(m_output.data(), &KScreen::Output::currentModeIdChanged, this, &QMLOutput::currentModeIdChanged); + connect(m_output.data(), &KScreen::Output::scaleChanged, + this, &QMLOutput::currentModeIdChanged); } QMLScreen *QMLOutput::screen() const { return m_screen; } void QMLOutput::setScreen(QMLScreen *screen) { - Q_ASSERT(m_screen == 0); + Q_ASSERT(m_screen == nullptr); m_screen = screen; Q_EMIT screenChanged(); } void QMLOutput::setLeftDockedTo(QMLOutput *output) { if (m_leftDock == output) { return; } m_leftDock = output; Q_EMIT leftDockedToChanged(); } QMLOutput *QMLOutput::leftDockedTo() const { return m_leftDock; } void QMLOutput::undockLeft() { setLeftDockedTo(0); } void QMLOutput::setTopDockedTo(QMLOutput *output) { if (m_topDock == output) { return; } m_topDock = output; Q_EMIT topDockedToChanged(); } QMLOutput *QMLOutput::topDockedTo() const { return m_topDock; } void QMLOutput::undockTop() { setTopDockedTo(0); } void QMLOutput::setRightDockedTo(QMLOutput *output) { if (m_rightDock == output) { return; } m_rightDock = output; Q_EMIT rightDockedToChanged(); } QMLOutput *QMLOutput::rightDockedTo() const { return m_rightDock; } void QMLOutput::undockRight() { setRightDockedTo(0); } void QMLOutput::setBottomDockedTo(QMLOutput *output) { if (m_bottomDock == output) { return; } m_bottomDock = output; Q_EMIT bottomDockedToChanged(); } QMLOutput *QMLOutput::bottomDockedTo() const { return m_bottomDock; } void QMLOutput::undockBottom() { setBottomDockedTo(0); } void QMLOutput::setCloneOf(QMLOutput* other) { if (m_cloneOf == other) { return; } m_cloneOf = other; Q_EMIT cloneOfChanged(); } QMLOutput* QMLOutput::cloneOf() const { return m_cloneOf; } int QMLOutput::currentOutputHeight() const { if (!m_output) { return 0; } KScreen::ModePtr mode = m_output->currentMode(); if (!mode) { if (m_output->isConnected()) { mode = bestMode(); if (!mode) { return 1000; } m_output->setCurrentModeId(mode->id()); } else { return 1000; } } - return mode->size().height(); + return mode->size().height() / m_output->scale(); } int QMLOutput::currentOutputWidth() const { if (!m_output) { return 0; } KScreen::ModePtr mode = m_output->currentMode(); if (!mode) { if (m_output->isConnected()) { mode = bestMode(); if (!mode) { return 1000; } m_output->setCurrentModeId(mode->id()); } else { return 1000; } } - return mode->size().width(); + return mode->size().width() / m_output->scale(); } void QMLOutput::currentModeIdChanged() { if (!m_output) { return; } - if (m_rightDock) { - QMLOutput *rightDock = m_rightDock; - float newWidth = currentOutputWidth() * m_screen->outputScale(); - setX(rightDock->x() - newWidth); - setRightDockedTo(rightDock); - } + if (isCloneMode()) { + const float newWidth = currentOutputWidth() * m_screen->outputScale(); + setX((m_screen->width() - newWidth) / 2); + const float newHeight = currentOutputHeight() * m_screen->outputScale(); + setY((m_screen->height() - newHeight) / 2); + } else { + if (m_rightDock) { + QMLOutput *rightDock = m_rightDock; + float newWidth = currentOutputWidth() * m_screen->outputScale(); + setX(rightDock->x() - newWidth); + setRightDockedTo(rightDock); + } - if (m_bottomDock) { - QMLOutput *bottomDock = m_bottomDock; - float newHeight = currentOutputHeight() * m_screen->outputScale(); - setY(bottomDock->y() - newHeight); - setBottomDockedTo(bottomDock); + if (m_bottomDock) { + QMLOutput *bottomDock = m_bottomDock; + float newHeight = currentOutputHeight() * m_screen->outputScale(); + setY(bottomDock->y() - newHeight); + setBottomDockedTo(bottomDock); + } } Q_EMIT currentOutputSizeChanged(); } int QMLOutput::outputX() const { return m_output->pos().x(); } void QMLOutput::setOutputX(int x) { if (m_output->pos().rx() == x) { return; } QPoint pos = m_output->pos(); pos.setX(x); m_output->setPos(pos); Q_EMIT outputXChanged(); } int QMLOutput::outputY() const { return m_output->pos().y(); } void QMLOutput::setOutputY(int y) { if (m_output->pos().ry() == y) { return; } QPoint pos = m_output->pos(); pos.setY(y); m_output->setPos(pos); Q_EMIT outputYChanged(); } bool QMLOutput::isCloneMode() const { return m_isCloneMode; } void QMLOutput::setIsCloneMode(bool isCloneMode) { if (m_isCloneMode == isCloneMode) { return; } m_isCloneMode = isCloneMode; Q_EMIT isCloneModeChanged(); } void QMLOutput::dockToNeighbours() { Q_FOREACH (QMLOutput *otherQmlOutput, m_screen->outputs()) { if (otherQmlOutput == this) { continue; } if (!otherQmlOutput->output()->isConnected() || !otherQmlOutput->output()->isEnabled()) { continue; } const QRect geom = m_output->geometry(); const QRect otherGeom = otherQmlOutput->output()->geometry(); if (geom.left() - 1 == otherGeom.right()) { setLeftDockedTo(otherQmlOutput); continue; } if (geom.right() + 1 == otherGeom.left()) { setRightDockedTo(otherQmlOutput); continue; } if (geom.top() - 1 == otherGeom.bottom()) { setTopDockedTo(otherQmlOutput); continue; } if (geom.bottom() + 1 == otherGeom.top()) { setBottomDockedTo(otherQmlOutput); continue; } } } KScreen::ModePtr QMLOutput::bestMode() const { if (!m_output) { return KScreen::ModePtr(); } KScreen::ModeList modes = m_output->modes(); KScreen::ModePtr bestMode; Q_FOREACH (const KScreen::ModePtr &mode, modes) { if (!bestMode || (mode->size() > bestMode->size())) { bestMode = mode; } } return bestMode; } bool QMLOutput::collidesWithOutput(QObject *other) { QQuickItem* otherItem = qobject_cast(other); return boundingRect().intersects(otherItem->boundingRect()); } bool QMLOutput::maybeSnapTo(QMLOutput *other) { qreal centerX = x() + (width() / 2.0); qreal centerY = y() + (height() / 2.0); const qreal x2 = other->x(); const qreal y2 = other->y(); const qreal height2 = other->height(); const qreal width2 = other->width(); const qreal centerX2 = x2 + (width2 / 2.0); const qreal centerY2 = y2 + (height2 / 2.0); /* left of other */ if ((x() + width() > x2 - sSnapArea) && (x() + width() < x2 + sSnapArea) && (y() + height() > y2) && (y() < y2 + height2)) { setX(x2 - width() + sMargin); centerX = x() + (width() / 2.0); setRightDockedTo(other); other->setLeftDockedTo(this); //output.cloneOf = null; /* output is snapped to other on left and their * upper sides are aligned */ if ((y() < y2 + sSnapAlignArea) && (y() > y2 - sSnapAlignArea)) { setY(y2); return true; } /* output is snapped to other on left and they * are centered */ if ((centerY < centerY2 + sSnapAlignArea) && (centerY > centerY2 - sSnapAlignArea)) { setY(centerY2 - (height() / 2.0)); return true; } /* output is snapped to other on left and their * bottom sides are aligned */ if ((y() + height() < y2 + height2 + sSnapAlignArea) && (y() + height() > y2 + height2 - sSnapAlignArea)) { setY(y2 + height2 - height()); return true; } return true; } /* output is right of other */ if ((x() > x2 + width2 - sSnapArea) && (x() < x2 + width2 + sSnapArea) && (y() + height() > y2) && (y() < y2 + height2)) { setX(x2 + width2 - sMargin); centerX = x() + (width() / 2.0); setLeftDockedTo(other); other->setRightDockedTo(this); //output.cloneOf = null; /* output is snapped to other on right and their * upper sides are aligned */ if ((y() < y2 + sSnapAlignArea) && (y() > y2 - sSnapAlignArea)) { setY(y2); return true; } /* output is snapped to other on right and they * are centered */ if ((centerY < centerY2 + sSnapAlignArea) && (centerY > centerY2 - sSnapAlignArea)) { setY(centerY2 - (height() / 2.0)); return true; } /* output is snapped to other on right and their * bottom sides are aligned */ if ((y() + height() < y2 + height2 + sSnapAlignArea) && (y() + height() > y2 + height2 - sSnapAlignArea)) { setY(y2 + height2 - height()); return true; } return true; } /* output is above other */ if ((y() + height() > y2 - sSnapArea) && (y() + height() < y2 + sSnapArea) && (x() + width() > x2) && (x() < x2 + width2)) { setY(y2 - height() + sMargin); centerY = y() + (height() / 2.0); setBottomDockedTo(other); other->setTopDockedTo(this); //output.cloneOf = null; /* output is snapped to other on top and their * left sides are aligned */ if ((x() < x2 + sSnapAlignArea) && (x() > x2 - sSnapAlignArea)) { setX(x2); return true; } /* output is snapped to other on top and they * are centered */ if ((centerX < centerX2 + sSnapAlignArea) && (centerX > centerX2 - sSnapAlignArea)) { setX(centerX2 - (width() / 2.0)); return true; } /* output is snapped to other on top and their * right sides are aligned */ if ((x() + width() < x2 + width2 + sSnapAlignArea) && (x() + width() > x2 + width2 - sSnapAlignArea)) { setX(x2 + width2 - width()); return true; } return true; } /* output is below other */ if ((y() > y2 + height2 - sSnapArea) && (y() < y2 + height2 + sSnapArea) && (x() + width() > x2) && (x() < x2 + width2)) { setY(y2 + height2 - sMargin); centerY = y() + (height() / 2.0); setTopDockedTo(other); other->setBottomDockedTo(this); //output.cloneOf = null; /* output is snapped to other on bottom and their * left sides are aligned */ if ((x() < x2 + sSnapAlignArea) && (x() > x2 - sSnapAlignArea)) { setX(x2); return true; } /* output is snapped to other on bottom and they * are centered */ if ((centerX < centerX2 + sSnapAlignArea) && (centerX > centerX2 - sSnapAlignArea)) { setX(centerX2 - (width() / 2.0)); return true; } /* output is snapped to other on bottom and their * right sides are aligned */ if ((x() + width() < x2 + width2 + sSnapAlignArea) && (x() + width() > x2 + width2 - sSnapAlignArea)) { setX(x2 + width2 - width()); return true; } return true; } return false; } void QMLOutput::moved() { const QList siblings = screen()->childItems(); // First, if we have moved, then unset the "cloneOf" flag setCloneOf(0); disconnect(this, &QMLOutput::xChanged, this, static_cast(&QMLOutput::moved)); disconnect(this, &QMLOutput::yChanged, this, static_cast(&QMLOutput::moved)); Q_FOREACH (QQuickItem *sibling, siblings) { QMLOutput *otherOutput = qobject_cast(sibling); if (!otherOutput || otherOutput == this) { continue; } if (!maybeSnapTo(otherOutput)) { if (m_leftDock == otherOutput) { m_leftDock->undockRight(); undockLeft(); } if (m_topDock == otherOutput) { m_topDock->undockBottom(); undockTop(); } if (m_rightDock == otherOutput) { m_rightDock->undockLeft(); undockRight(); } if (m_bottomDock == otherOutput) { m_bottomDock->undockTop(); undockBottom(); } } } connect(this, &QMLOutput::xChanged, this, static_cast(&QMLOutput::moved)); connect(this, &QMLOutput::yChanged, this, static_cast(&QMLOutput::moved)); Q_EMIT moved(m_output->name()); } /* Transformation of an item (rotation of the MouseArea) is only visual. * The coordinates and dimensions are still the same (when you rotated * 100x500 rectangle by 90 deg, it will still be 100x500, although * visually it will be 500x100). * * This method calculates the real-visual coordinates and dimensions of * the MouseArea and updates root item to match them. This makes snapping * works correctly regardless on visual rotation of the output */ void QMLOutput::updateRootProperties() { const float transformedWidth = (m_output->isHorizontal() ? currentOutputWidth() : currentOutputHeight()) * m_screen->outputScale(); const float transformedHeight = (m_output->isHorizontal() ? currentOutputHeight() : currentOutputWidth()) * m_screen->outputScale(); const float transformedX = x() + (width() / 2.0) - (transformedWidth / 2.0); const float transformedY = y() + (height() / 2.0) - (transformedHeight / 2.0); setPosition(QPointF(transformedX, transformedY)); setSize(QSizeF(transformedWidth, transformedHeight)); } diff --git a/kcm/src/declarative/qmloutputcomponent.cpp b/kcm/src/declarative/qmloutputcomponent.cpp index 98bf832..4dc6838 100644 --- a/kcm/src/declarative/qmloutputcomponent.cpp +++ b/kcm/src/declarative/qmloutputcomponent.cpp @@ -1,65 +1,62 @@ /* * Copyright (C) 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) 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 "qmloutputcomponent.h" #include "qmloutput.h" #include "qmlscreen.h" #include #include #include #include Q_DECLARE_METATYPE(KScreen::OutputPtr) Q_DECLARE_METATYPE(QMLScreen*) QMLOutputComponent::QMLOutputComponent(QQmlEngine *engine, QMLScreen *parent): QQmlComponent(engine, parent), m_engine(engine) { const QString qmlPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_kscreen/qml/Output.qml")); loadUrl(QUrl::fromLocalFile(qmlPath)); } QMLOutputComponent::~QMLOutputComponent() { } QMLOutput* QMLOutputComponent::createForOutput(const KScreen::OutputPtr &output) { - QObject *instance; - - instance = beginCreate(m_engine->rootContext()); + QObject *instance = beginCreate(m_engine->rootContext()); if (!instance) { qWarning() << errorString(); - return 0; + return nullptr; } - bool success = false; - success = instance->setProperty("outputPtr", QVariant::fromValue(output)); + bool success = instance->setProperty("outputPtr", QVariant::fromValue(output)); Q_ASSERT(success); success = instance->setProperty("screen", QVariant::fromValue(qobject_cast(parent()))); Q_ASSERT(success); Q_UNUSED(success); completeCreate(); return qobject_cast(instance); } diff --git a/kcm/src/declarative/qmlscreen.cpp b/kcm/src/declarative/qmlscreen.cpp index 6f44312..6103c43 100644 --- a/kcm/src/declarative/qmlscreen.cpp +++ b/kcm/src/declarative/qmlscreen.cpp @@ -1,362 +1,366 @@ /* * Copyright (C) 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) 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 "qmlscreen.h" #include "qmloutputcomponent.h" #include "qmloutput.h" #include #include #include #include #include //static void NullDeleter(KScreen::Output */*output*/) { } QMLScreen::QMLScreen(QQuickItem *parent): QQuickItem(parent), - m_config(0), + m_config(nullptr), m_connectedOutputsCount(0), m_enabledOutputsCount(0), - m_leftmost(0), - m_topmost(0), - m_rightmost(0), - m_bottommost(0) + m_leftmost(nullptr), + m_topmost(nullptr), + m_rightmost(nullptr), + m_bottommost(nullptr) { connect(this, &QMLScreen::widthChanged, this, &QMLScreen::viewSizeChanged); connect(this, &QMLScreen::heightChanged, this, &QMLScreen::viewSizeChanged); } QMLScreen::~QMLScreen() { } KScreen::ConfigPtr QMLScreen::config() const { return m_config; } void QMLScreen::setConfig(const KScreen::ConfigPtr &config) { qDeleteAll(m_outputMap); m_outputMap.clear(); m_bottommost = m_leftmost = m_rightmost = m_topmost = 0; m_connectedOutputsCount = 0; m_enabledOutputsCount = 0; if (m_config) { m_config->disconnect(this); } m_config = config; connect(m_config.data(), &KScreen::Config::outputAdded, this, [this](const KScreen::OutputPtr &output) { addOutput(output); updateOutputsPlacement(); }); connect(m_config.data(), &KScreen::Config::outputRemoved, this, &QMLScreen::removeOutput); for (const KScreen::OutputPtr &output : m_config->outputs()) { addOutput(output); } updateOutputsPlacement(); for (QMLOutput *qmlOutput : m_outputMap) { if (qmlOutput->output()->isConnected() && qmlOutput->output()->isEnabled()) { qmlOutput->dockToNeighbours(); } } } void QMLScreen::addOutput(const KScreen::OutputPtr &output) { //QQuickItem *container = findChild(QLatin1String("outputContainer")); QMLOutputComponent comp(m_engine, this); QMLOutput *qmloutput = comp.createForOutput(output); if (!qmloutput) { qWarning() << "Failed to create QMLOutput"; return; } m_outputMap.insert(output, qmloutput); qmloutput->setParentItem(this); qmloutput->setZ(m_outputMap.count()); connect(output.data(), &KScreen::Output::isConnectedChanged, this, &QMLScreen::outputConnectedChanged); connect(output.data(), &KScreen::Output::isEnabledChanged, this, &QMLScreen::outputEnabledChanged); connect(output.data(), &KScreen::Output::posChanged, this, &QMLScreen::outputPositionChanged); connect(qmloutput, &QMLOutput::yChanged, [this, qmloutput]() { qmlOutputMoved(qmloutput); }); connect(qmloutput, &QMLOutput::xChanged, [this, qmloutput]() { qmlOutputMoved(qmloutput); }); connect(qmloutput, SIGNAL(clicked()), this, SLOT(setActiveOutput())); qmloutput->updateRootProperties(); } void QMLScreen::removeOutput(int outputId) { for (const KScreen::OutputPtr &output : m_outputMap.keys()) { if (output->id() == outputId) { QMLOutput *qmlOutput = m_outputMap.take(output); qmlOutput->setParentItem(Q_NULLPTR); qmlOutput->setParent(Q_NULLPTR); qmlOutput->deleteLater(); return; } } } int QMLScreen::connectedOutputsCount() const { return m_connectedOutputsCount; } int QMLScreen::enabledOutputsCount() const { return m_enabledOutputsCount; } QMLOutput *QMLScreen::primaryOutput() const { Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) { if (qmlOutput->output()->isPrimary()) { return qmlOutput; } } - return 0; + return nullptr; } QList QMLScreen::outputs() const { return m_outputMap.values(); } void QMLScreen::setActiveOutput(QMLOutput *output) { Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) { if (qmlOutput->z() > output->z()) { qmlOutput->setZ(qmlOutput->z() - 1); } } output->setZ(m_outputMap.count()); output->setFocus(true); Q_EMIT focusedOutputChanged(output); } QSize QMLScreen::maxScreenSize() const { return m_config->screen()->maxSize(); } float QMLScreen::outputScale() const { return 1.0 / 8.0; } void QMLScreen::outputConnectedChanged() { int connectedCount = 0; Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) { if (output->isConnected()) { ++connectedCount; } } if (connectedCount != m_connectedOutputsCount) { m_connectedOutputsCount = connectedCount; Q_EMIT connectedOutputsCountChanged(); updateOutputsPlacement(); } } void QMLScreen::outputEnabledChanged() { const KScreen::OutputPtr output(qobject_cast(sender()), [](void *){}); if (output->isEnabled()) { updateOutputsPlacement(); } int enabledCount = 0; Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) { if (output->isEnabled()) { ++enabledCount; } } if (enabledCount == m_enabledOutputsCount) { m_enabledOutputsCount = enabledCount; Q_EMIT enabledOutputsCountChanged(); } } void QMLScreen::outputPositionChanged() { /* TODO: Reposition the QMLOutputs */ } void QMLScreen::qmlOutputMoved(QMLOutput *qmlOutput) { + if (qmlOutput->isCloneMode()) { + return; + } + updateCornerOutputs(); if (m_leftmost) { m_leftmost->setOutputX(0); } if (m_topmost) { m_topmost->setOutputY(0); } if (qmlOutput == m_leftmost) { Q_FOREACH (QMLOutput *other, m_outputMap) { if (other == m_leftmost) { continue; } if (!other->output()->isConnected() || !other->output()->isEnabled()) { continue; } other->setOutputX(float(other->x() - m_leftmost->x()) / outputScale()); } } else if (m_leftmost) { qmlOutput->setOutputX(float(qmlOutput->x() - m_leftmost->x()) / outputScale()); } if (qmlOutput == m_topmost) { Q_FOREACH (QMLOutput *other, m_outputMap) { if (other == m_topmost) { continue; } if (!other->output()->isConnected() || !other->output()->isEnabled()) { continue; } other->setOutputY(float(other->y() - m_topmost->y()) / outputScale()); } } else if (m_topmost) { qmlOutput->setOutputY(float(qmlOutput->y() - m_topmost->y()) / outputScale()); } } void QMLScreen::viewSizeChanged() { updateOutputsPlacement(); } void QMLScreen::updateCornerOutputs() { m_leftmost = 0; m_topmost = 0; m_rightmost = 0; m_bottommost = 0; Q_FOREACH (QMLOutput *output, m_outputMap) { if (!output->output()->isConnected() || !output->output()->isEnabled()) { continue; } QMLOutput *other = m_leftmost; if (!other || output->x() < other->x()) { m_leftmost = output; } if (!other || output->y() < other->y()) { m_topmost = output; } if (!other || output->x() + output->width() > other->x() + other->width()) { m_rightmost = output; } if (!other || output->y() + output->height() > other->y() + other->height()) { m_bottommost = output; } } } void QMLScreen::updateOutputsPlacement() { int disabledOffsetX = width(); QSizeF activeScreenSize; Q_FOREACH (QQuickItem *item, childItems()) { QMLOutput *qmlOutput = qobject_cast(item); if (!qmlOutput->output()->isConnected()) { continue; } if (!qmlOutput->output()->isEnabled()) { qmlOutput->blockSignals(true); disabledOffsetX -= qmlOutput->width(); qmlOutput->setPosition(QPoint(disabledOffsetX, 0)); qmlOutput->blockSignals(false); continue; } if (qmlOutput->outputX() + qmlOutput->currentOutputWidth() > activeScreenSize.width()) { activeScreenSize.setWidth(qmlOutput->outputX() + qmlOutput->currentOutputWidth()); } if (qmlOutput->outputY() + qmlOutput->currentOutputHeight() > activeScreenSize.height()) { activeScreenSize.setHeight(qmlOutput->outputY() + qmlOutput->currentOutputHeight()); } } activeScreenSize *= outputScale(); const QPointF offset((width() - activeScreenSize.width()) / 2.0, (height() - activeScreenSize.height()) / 2.0); Q_FOREACH (QQuickItem *item, childItems()) { QMLOutput *qmlOutput = qobject_cast(item); if (!qmlOutput->output()->isConnected() || !qmlOutput->output()->isEnabled()) { continue; } qmlOutput->blockSignals(true); qmlOutput->setPosition(QPointF(offset.x() + (qmlOutput->outputX() * outputScale()), offset.y() + (qmlOutput->outputY() * outputScale()))); qmlOutput->blockSignals(false); } } void QMLScreen::setEngine(QQmlEngine* engine) { m_engine = engine; } diff --git a/kcm/src/declarative/qmlscreen.h b/kcm/src/declarative/qmlscreen.h index d74fbbc..1c3d54b 100644 --- a/kcm/src/declarative/qmlscreen.h +++ b/kcm/src/declarative/qmlscreen.h @@ -1,119 +1,122 @@ /* * Copyright (C) 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) 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. * */ #ifndef QMLSCREEN_H #define QMLSCREEN_H #include #include #include "qmloutput.h" class QQmlEngine; namespace KScreen { class Output; class Config; } class QMLScreen : public QQuickItem { Q_OBJECT Q_PROPERTY(QSize maxScreenSize READ maxScreenSize CONSTANT) Q_PROPERTY(int connectedOutputsCount READ connectedOutputsCount NOTIFY connectedOutputsCountChanged) Q_PROPERTY(int enabledOutputsCount READ enabledOutputsCount NOTIFY enabledOutputsCountChanged) Q_PROPERTY(float outputScale READ outputScale NOTIFY outputScaleChanged) Q_PROPERTY(QQmlEngine* engine MEMBER m_engine) public: - explicit QMLScreen(QQuickItem *parent = 0); + explicit QMLScreen(QQuickItem *parent = nullptr); virtual ~QMLScreen(); int connectedOutputsCount() const; int enabledOutputsCount() const; QMLOutput* primaryOutput() const; QList outputs() const; QSize maxScreenSize() const; float outputScale() const; KScreen::ConfigPtr config() const; void setConfig(const KScreen::ConfigPtr &config); void updateOutputsPlacement(); void setEngine(QQmlEngine* engine); void setActiveOutput(QMLOutput *output); public Q_SLOTS: void setActiveOutput() { setActiveOutput(qobject_cast(sender())); } Q_SIGNALS: void connectedOutputsCountChanged(); void enabledOutputsCountChanged(); void outputScaleChanged(); void focusedOutputChanged(QMLOutput *output); private Q_SLOTS: void addOutput(const KScreen::OutputPtr &output); void removeOutput(int outputId); void outputConnectedChanged(); void outputEnabledChanged(); void outputPositionChanged(); void viewSizeChanged(); private: void qmlOutputMoved(QMLOutput *qmlOutput); void updateCornerOutputs(); KScreen::ConfigPtr m_config; QHash m_outputMap; int m_connectedOutputsCount; int m_enabledOutputsCount; - QQmlEngine* m_engine; - QMLOutput *m_leftmost, *m_topmost, *m_rightmost, *m_bottommost; + QQmlEngine* m_engine = nullptr; + QMLOutput *m_leftmost = nullptr; + QMLOutput *m_topmost = nullptr; + QMLOutput *m_rightmost = nullptr; + QMLOutput *m_bottommost = nullptr; }; #endif // QMLSCREEN_H diff --git a/kcm/src/kcm_kscreen.cpp b/kcm/src/kcm_kscreen.cpp index c10c3e9..4a0498b 100644 --- a/kcm/src/kcm_kscreen.cpp +++ b/kcm/src/kcm_kscreen.cpp @@ -1,179 +1,187 @@ /* Copyright (C) 2012 Dan Vratil 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 "kcm_kscreen.h" -#include "debug.h" +#include "kcm_screen_debug.h" #include "widget.h" +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(KCMDisplayConfigurationFactory, registerPlugin();) using namespace KScreen; Q_DECLARE_METATYPE(KScreen::OutputPtr) Q_DECLARE_METATYPE(KScreen::ScreenPtr) KCMKScreen::KCMKScreen(QWidget* parent, const QVariantList& args) : KCModule(parent, args) - , mKScreenWidget(0) + , mKScreenWidget(nullptr) { Log::instance(); setButtons(Apply | Default); KAboutData* about = new KAboutData(QStringLiteral("kcm_kscreen"), i18n("Display Configuration"), QStringLiteral(KSCREEN_VERSION), i18n("Configuration for displays"), KAboutLicense::GPL_V2, i18n("(c), 2012-2013 Daniel Vrátil")); about->addAuthor(i18n("Daniel Vrátil"), i18n("Maintainer") , QStringLiteral("dvratil@redhat.com")); setAboutData(about); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); } void KCMKScreen::configReady(ConfigOperation* op) { - QHBoxLayout *layout = new QHBoxLayout(this); + delete mMainLayout; + mMainLayout = new QHBoxLayout(this); + mMainLayout->setMargin(0); if (op->hasError()) { - mKScreenWidget = 0; + mKScreenWidget = nullptr; delete mKScreenWidget; QLabel *errorLabel = new QLabel(this); - layout->addWidget(errorLabel); + mMainLayout->addWidget(errorLabel); errorLabel->setText(i18n("No kscreen backend found. Please check your kscreen installation.")); return; } if (!mKScreenWidget) { mKScreenWidget = new Widget(this); - layout->addWidget(mKScreenWidget); + mMainLayout->addWidget(mKScreenWidget); QObject::connect(mKScreenWidget, &Widget::changed, this, &KCMKScreen::changed); } mKScreenWidget->setConfig(qobject_cast(op)->config()); } KCMKScreen::~KCMKScreen() { } +QSize KCMKScreen::sizeHint() const +{ + return QSize(0, 700); +} + void KCMKScreen::changed() { if (!m_blockChanges) { KCModule::changed(); } } void KCMKScreen::save() { qCDebug(KSCREEN_KCM) << "Saving."; if (!mKScreenWidget) { return; } const KScreen::ConfigPtr &config = mKScreenWidget->currentConfig(); bool atLeastOneEnabledOutput = false; Q_FOREACH(const KScreen::OutputPtr &output, config->outputs()) { KScreen::ModePtr mode = output->currentMode(); if (output->isEnabled()) { atLeastOneEnabledOutput = true; } qCDebug(KSCREEN_KCM) << output->name() << output->id() << output.data() << "\n" << " Connected:" << output->isConnected() << "\n" << " Enabled:" << output->isEnabled() << "\n" << " Primary:" << output->isPrimary() << "\n" << " Rotation:" << output->rotation() << "\n" << " Mode:" << (mode ? mode->name() : QStringLiteral("unknown")) << "@" << (mode ? mode->refreshRate() : 0.0) << "Hz" << "\n" << " Position:" << output->pos().x() << "x" << output->pos().y(); } if (!atLeastOneEnabledOutput) { if (KMessageBox::warningYesNo(this, i18n("Are you sure you want to disable all outputs?"), i18nc("@title:window", "Disable All Outputs"), KGuiItem(i18n("&Disable All Outputs"), QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))), KGuiItem(i18n("&Reconfigure"), QIcon::fromTheme(QStringLiteral("dialog-cancel"))), QString(), KMessageBox::Dangerous) == KMessageBox::No) { return; } } if (!Config::canBeApplied(config)) { KMessageBox::information(this, i18n("Sorry, your configuration could not be applied.\n\n" "Common reasons are that the overall screen size is too big, or you enabled more displays than supported by your GPU."), i18nc("@title:window", "Unsupported Configuration")); return; } m_blockChanges = true; /* Store the current config, apply settings */ auto *op = new SetConfigOperation(config); /* Block until the operation is completed, otherwise KCMShell will terminate * before we get to execute the Operation */ op->exec(); // The 1000ms is a bit "random" here, it's what works on the systems I've tested, but ultimately, this is a hack // due to the fact that we just can't be sure when xrandr is done changing things, 1000 doesn't seem to get in the way QTimer::singleShot(1000, this, [this] () { m_blockChanges = false; } ); } void KCMKScreen::defaults() { - qCDebug(KSCREEN_KCM) << "LOAD"; + qCDebug(KSCREEN_KCM) << "APPLY DEFAULT"; load(); } void KCMKScreen::load() { qCDebug(KSCREEN_KCM) << "LOAD"; connect(new GetConfigOperation(), &GetConfigOperation::finished, this, &KCMKScreen::configReady); } #include "kcm_kscreen.moc" diff --git a/kcm/src/kcm_kscreen.h b/kcm/src/kcm_kscreen.h index 0cecb9f..fc72ab8 100644 --- a/kcm/src/kcm_kscreen.h +++ b/kcm/src/kcm_kscreen.h @@ -1,54 +1,57 @@ /* Copyright (C) 2012 Dan Vratil 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 */ #ifndef KCM_KSCREEN_H #define KCM_KSCREEN_H #include class Widget; class QTimer; - +class QHBoxLayout; namespace KScreen { class ConfigOperation; } class KCMKScreen : public KCModule { Q_OBJECT public: - explicit KCMKScreen (QWidget* parent = 0, const QVariantList& args = QVariantList()); + explicit KCMKScreen (QWidget* parent = nullptr, const QVariantList& args = QVariantList()); virtual ~KCMKScreen(); + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + public Q_SLOTS: - virtual void load(); - virtual void save(); - virtual void defaults(); + void load() Q_DECL_OVERRIDE; + void save() Q_DECL_OVERRIDE; + void defaults() Q_DECL_OVERRIDE; void changed(); private: void configReady(KScreen::ConfigOperation *op); - Widget *mKScreenWidget; + Widget *mKScreenWidget = nullptr; bool m_blockChanges = false; + QHBoxLayout *mMainLayout = nullptr; }; #endif // DisplayConfiguration_H diff --git a/kcm/src/outputconfig.cpp b/kcm/src/outputconfig.cpp index d02a286..7180a0d 100644 --- a/kcm/src/outputconfig.cpp +++ b/kcm/src/outputconfig.cpp @@ -1,232 +1,263 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "outputconfig.h" #include "resolutionslider.h" #include "collapsablebutton.h" #include "utils.h" -#include "debug.h" +#include "kcm_screen_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include OutputConfig::OutputConfig(QWidget *parent) : QWidget(parent) - , mOutput(0) + , mOutput(nullptr) { } OutputConfig::OutputConfig(const KScreen::OutputPtr &output, QWidget *parent) : QWidget(parent) { setOutput(output); } OutputConfig::~OutputConfig() { } void OutputConfig::setTitle(const QString& title) { mTitle->setText(title); } void OutputConfig::initUi() { connect(mOutput.data(), &KScreen::Output::isConnectedChanged, this, [=]() { if (!mOutput->isConnected()) { setVisible(false); } }); connect(mOutput.data(), &KScreen::Output::isEnabledChanged, this, [=]() { - mEnabled->blockSignals(true); mEnabled->setChecked(mOutput->isEnabled()); - mEnabled->blockSignals(false); }); connect(mOutput.data(), &KScreen::Output::rotationChanged, this, [=]() { const int index = mRotation->findData(mOutput->rotation()); - mRotation->blockSignals(true); mRotation->setCurrentIndex(index); - mRotation->blockSignals(false); + }); + + connect(mOutput.data(), &KScreen::Output::scaleChanged, + this, [=]() { + const int index = mScale->findData(mOutput->scale()); + mScale->setCurrentIndex(index); }); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QVBoxLayout *vbox = new QVBoxLayout(this); mTitle = new QLabel(this); mTitle->setAlignment(Qt::AlignHCenter); vbox->addWidget(mTitle); setTitle(Utils::outputName(mOutput)); QFormLayout *formLayout = new QFormLayout(); vbox->addLayout(formLayout); mEnabled = new QCheckBox(i18n("Enabled"), this); mEnabled->setChecked(mOutput->isEnabled()); connect(mEnabled, &QCheckBox::clicked, this, [=](bool checked) { mOutput->setEnabled(checked); qCDebug(KSCREEN_KCM) << mOutput.data() << mOutput->name() << mOutput->isEnabled(); Q_EMIT changed(); }); formLayout->addRow(i18n("Display:"), mEnabled); mResolution = new ResolutionSlider(mOutput, this); connect(mResolution, &ResolutionSlider::resolutionChanged, this, &OutputConfig::slotResolutionChanged); formLayout->addRow(i18n("Resolution:"), mResolution); mRotation = new QComboBox(this); mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-up")), i18n("Normal"), KScreen::Output::None); - mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("90° Clockwise"), KScreen::Output::Left); + mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-right")), i18n("90° Clockwise"), KScreen::Output::Right); mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-down")), i18n("Upside Down"), KScreen::Output::Inverted); - mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-right")), i18n("90° Counterclockwise"), KScreen::Output::Right); - connect(mRotation, static_cast(&QComboBox::currentIndexChanged), + mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("90° Counterclockwise"), KScreen::Output::Left); + connect(mRotation, static_cast(&QComboBox::activated), this, &OutputConfig::slotRotationChanged); mRotation->setCurrentIndex(mRotation->findData(mOutput->rotation())); formLayout->addRow(i18n("Orientation:"), mRotation); - formLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + if (mShowScaleOption) { + mScale = new QComboBox(this); + mScale->addItem(i18nc("Scale multiplier, show everything at 1 times normal scale", "1x"), 1); + mScale->addItem(i18nc("Scale multiplier, show everything at 2 times normal scale", "2x"), 2); + connect(mScale, static_cast(&QComboBox::activated), + this, &OutputConfig::slotScaleChanged); + mScale->setCurrentIndex(mScale->findData(mOutput->scale())); + + formLayout->addRow(i18n("Scale:"), mScale); + + formLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + } CollapsableButton *advancedButton = new CollapsableButton(i18n("Advanced Settings"), this); advancedButton->setCollapsed(true); vbox->addWidget(advancedButton); QWidget *advancedWidget = new QWidget(this); int leftMargin, topMargin, rightMargin, bottomMargin; advancedWidget->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); advancedWidget->setContentsMargins(25, topMargin, rightMargin, bottomMargin); vbox->addWidget(advancedWidget); advancedButton->setWidget(advancedWidget); formLayout = new QFormLayout(advancedWidget); advancedWidget->setLayout(formLayout); mRefreshRate = new QComboBox(advancedWidget); mRefreshRate->addItem(i18n("Auto"), -1); formLayout->addRow(i18n("Refresh rate:"), mRefreshRate); slotResolutionChanged(mResolution->currentResolution()); - connect(mRefreshRate, static_cast(&QComboBox::currentIndexChanged), + connect(mRefreshRate, static_cast(&QComboBox::activated), this, &OutputConfig::slotRefreshRateChanged); vbox->addStretch(2); } void OutputConfig::setOutput(const KScreen::OutputPtr &output) { mOutput = output; initUi(); } KScreen::OutputPtr OutputConfig::output() const { return mOutput; } void OutputConfig::slotResolutionChanged(const QSize &size) { // Ignore disconnected outputs if (!size.isValid()) { return; } KScreen::ModePtr selectedMode; QList modes; - Q_FOREACH (const KScreen::ModePtr mode, mOutput->modes()) { + Q_FOREACH (const KScreen::ModePtr &mode, mOutput->modes()) { if (mode->size() == size) { modes << mode; if (!selectedMode || selectedMode->refreshRate() < mode->refreshRate()) { selectedMode = mode; } } } Q_ASSERT(selectedMode); mOutput->setCurrentModeId(selectedMode->id()); // Don't remove the first "Auto" item - prevents ugly flicker of the combobox // when changing resolution - mRefreshRate->blockSignals(true); for (int i = 1; i < mRefreshRate->count(); ++i) { mRefreshRate->removeItem(i); } - for (int i = 0; i < modes.count(); i++) { + for (int i = 0, total = modes.count(); i < total; ++i) { const KScreen::ModePtr mode = modes.at(i); - mRefreshRate->addItem(i18n("%1 Hz", QString::fromLatin1("%1").arg(mode->refreshRate(), 0, 'f', 2)), mode->id()); + mRefreshRate->addItem(i18n("%1 Hz", QLocale().toString(mode->refreshRate(), 'f', 2)), mode->id()); // If selected refresh rate is other then what we consider the "Auto" value // - that is it's not the highest resolution - then select it, otherwise // we stick with "Auto" if (mode == selectedMode && i > 1) { mRefreshRate->setCurrentIndex(i); } } - mRefreshRate->blockSignals(false); Q_EMIT changed(); } void OutputConfig::slotRotationChanged(int index) { - KScreen::Output::Rotation rotation = + KScreen::Output::Rotation rotation = static_cast(mRotation->itemData(index).toInt()); mOutput->setRotation(rotation); Q_EMIT changed(); } void OutputConfig::slotRefreshRateChanged(int index) { QString modeId; if (index == 0) { // Item 0 is "Auto" - "Auto" is equal to highest refresh rate (at least // that's how I understand it, and since the combobox is sorted in descending // order, we just pick the second item from top modeId = mRefreshRate->itemData(1).toString(); } else { modeId = mRefreshRate->itemData(index).toString(); } mOutput->setCurrentModeId(modeId); Q_EMIT changed(); } + +void OutputConfig::slotScaleChanged(int index) +{ + auto scale = mScale->itemData(index).toInt(); + mOutput->setScale(scale); + Q_EMIT changed(); +} + +void OutputConfig::setShowScaleOption(bool showScaleOption) +{ + mShowScaleOption = showScaleOption; + if (mOutput) { + initUi(); + } +} + +bool OutputConfig::showScaleOption() const +{ + return mShowScaleOption; +} diff --git a/kcm/src/outputconfig.h b/kcm/src/outputconfig.h index aebc99e..b85dfda 100644 --- a/kcm/src/outputconfig.h +++ b/kcm/src/outputconfig.h @@ -1,70 +1,76 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef OUTPUTCONFIG_H #define OUTPUTCONFIG_H #include #include #include #include class CollapsableButton; class QCheckBox; class ResolutionSlider; class QLabel; class OutputConfig : public QWidget { Q_OBJECT public: explicit OutputConfig(QWidget *parent); - explicit OutputConfig(const KScreen::OutputPtr &output, QWidget *parent = 0); + explicit OutputConfig(const KScreen::OutputPtr &output, QWidget *parent = nullptr); virtual ~OutputConfig(); virtual void setOutput(const KScreen::OutputPtr &output); KScreen::OutputPtr output() const; void setTitle(const QString &title); + void setShowScaleOption(bool showScaleOption); + bool showScaleOption() const; + protected Q_SLOTS: void slotResolutionChanged(const QSize &size); void slotRotationChanged(int index); void slotRefreshRateChanged(int index); + void slotScaleChanged(int index); Q_SIGNALS: void changed(); protected: virtual void initUi(); protected: - QLabel *mTitle; + QLabel *mTitle = nullptr; KScreen::OutputPtr mOutput; - QCheckBox *mEnabled; - ResolutionSlider *mResolution; - QComboBox *mRotation; - QComboBox *mRefreshRate; + QCheckBox *mEnabled = nullptr; + ResolutionSlider *mResolution = nullptr; + QComboBox *mRotation = nullptr; + QComboBox *mScale = nullptr; + QComboBox *mRefreshRate = nullptr; + bool mShowScaleOption = false; }; #endif // OUTPUTCONFIG_H diff --git a/kcm/src/previewwidget.cpp b/kcm/src/previewwidget.cpp index aa236bb..3a7341e 100644 --- a/kcm/src/previewwidget.cpp +++ b/kcm/src/previewwidget.cpp @@ -1,90 +1,87 @@ /* * Copyright (C) 2015 David Edmundson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "previewwidget.h" #include #include #include "ui_stylepreview.h" PreviewWidget::PreviewWidget(QWidget *parent): QLabel(parent), m_scale(1.0), m_internalPreview(new QWidget) // deliberately no parent, we don't want it to have a screen { Ui::StylePreview ui; - ui.setupUi(m_internalPreview ); + ui.setupUi(m_internalPreview); } PreviewWidget::~PreviewWidget() { delete m_internalPreview; } void PreviewWidget::setScale(qreal scale) { m_scale = scale; QFont font; //take the user's configured point size, and convert it to a pixel size for preview font.setPixelSize(pointSizeToPixelSize(font.pointSize())); m_internalPreview->setFont(font); //as we are a hidden widget, we need to force a repaint to update the size hint properly updatePixmapCache(); m_internalPreview->resize(sizeHint()); m_internalPreview->adjustSize(); QPixmap preview = updatePixmapCache(); setPixmap(preview); } qreal PreviewWidget::pointSizeToPixelSize(qreal pointSize) const { //point size is in how many 72ths of an inch it should be, default DPI is 96 qreal pixelSize = pointSize * 96.0 / 72.0; //scale by our new factor pixelSize *= m_scale; - int dpr = qRound(m_scale); - return pixelSize / dpr; //as we are now dealing with pixels it will be scaled up in the paint(), so it needs dividing here + return pixelSize / m_scale; //as we are now dealing with pixels it will be scaled up in the paint(), so it needs dividing here } QPixmap PreviewWidget::updatePixmapCache() { - int dpr = qRound(m_scale); - - QPixmap pixmap(m_internalPreview ->sizeHint() * dpr); - pixmap.setDevicePixelRatio(dpr); + QPixmap pixmap(m_internalPreview ->sizeHint() * m_scale); + pixmap.setDevicePixelRatio(m_scale); QPainter p(&pixmap); m_internalPreview ->render(&p); //render back at whatever the native DPR of the KCM is pixmap.setDevicePixelRatio(devicePixelRatio()); return pixmap; } diff --git a/kcm/src/previewwidget.h b/kcm/src/previewwidget.h index f0c07a8..7b81e9c 100644 --- a/kcm/src/previewwidget.h +++ b/kcm/src/previewwidget.h @@ -1,42 +1,42 @@ /* * Copyright (C) 2015 David Edmundson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef PREVIEWWIDGET_H #define PREVIEWWIDGET_H #include class PreviewWidget : public QLabel { Q_OBJECT public: - PreviewWidget(QWidget *parent=0); + explicit PreviewWidget(QWidget *parent=nullptr); ~PreviewWidget(); void setScale(qreal scale); public Q_SLOTS: QPixmap updatePixmapCache(); private: qreal pointSizeToPixelSize(qreal pointSize) const; qreal m_scale; - QWidget *m_internalPreview; + QWidget *m_internalPreview = nullptr; }; #endif // PREVIEWWIDGET_H diff --git a/kcm/src/primaryoutputcombo.h b/kcm/src/primaryoutputcombo.h index 8077534..eb7a223 100644 --- a/kcm/src/primaryoutputcombo.h +++ b/kcm/src/primaryoutputcombo.h @@ -1,58 +1,58 @@ /* * Copyright 2015 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef PRIMARYOUTPUTCOMBO_H #define PRIMARYOUTPUTCOMBO_H #include #include class PrimaryOutputCombo : public QComboBox { Q_OBJECT public: - explicit PrimaryOutputCombo(QWidget *parent = 0); + explicit PrimaryOutputCombo(QWidget *parent = nullptr); virtual ~PrimaryOutputCombo(); void setConfig(const KScreen::ConfigPtr &config); KScreen::OutputPtr primaryOutput() const; Q_SIGNALS: void changed(); private Q_SLOTS: void addOutput(const KScreen::OutputPtr &output); void removeOutput(int outputId); void setPrimaryOutput(const KScreen::OutputPtr &output); void outputChanged(const KScreen::OutputPtr &output); void onCurrentIndexChanged(int currentIndex); private: void addOutputItem(const KScreen::OutputPtr &output); void removeOutputItem(int outputId); private: KScreen::ConfigPtr mConfig; }; -#endif \ No newline at end of file +#endif diff --git a/kcm/src/resolutionslider.cpp b/kcm/src/resolutionslider.cpp index c1590b3..5769f0a 100644 --- a/kcm/src/resolutionslider.cpp +++ b/kcm/src/resolutionslider.cpp @@ -1,165 +1,165 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "resolutionslider.h" #include "utils.h" #include #include #include #include #include #include static bool sizeLessThan(const QSize &sizeA, const QSize &sizeB) { return sizeA.width() * sizeA.height() < sizeB.width() * sizeB.height(); } ResolutionSlider::ResolutionSlider(const KScreen::OutputPtr &output, QWidget *parent) : QWidget(parent) , mOutput(output) - , mSmallestLabel(0) - , mBiggestLabel(0) - , mCurrentLabel(0) - , mSlider(0) - , mComboBox(0) + , mSmallestLabel(nullptr) + , mBiggestLabel(nullptr) + , mCurrentLabel(nullptr) + , mSlider(nullptr) + , mComboBox(nullptr) { connect(output.data(), &KScreen::Output::currentModeIdChanged, this, &ResolutionSlider::slotOutputModeChanged); Q_FOREACH (const KScreen::ModePtr &mode, output->modes()) { if (mModes.contains(mode->size())) { continue; } mModes << mode->size(); } qSort(mModes.begin(), mModes.end(), sizeLessThan); QGridLayout *layout = new QGridLayout(this); if (mModes.count() > 15) { std::reverse(mModes.begin(), mModes.end()); mComboBox = new QComboBox(this); mComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); mComboBox->setEditable(false); Q_FOREACH (const QSize &size, mModes) { mComboBox->addItem(Utils::sizeToString(size)); if ((output->currentMode() && (output->currentMode()->size() == size)) || (output->preferredMode() && (output->preferredMode()->size() == size))) { mComboBox->setCurrentIndex(mComboBox->count() - 1); } } layout->addWidget(mComboBox, 0, 0, 1, 1); connect(mComboBox, static_cast(&QComboBox::currentIndexChanged), this, &ResolutionSlider::slotValueChanged); } else { mCurrentLabel = new QLabel(this); mCurrentLabel->setAlignment(Qt::AlignCenter); layout->addWidget(mCurrentLabel, 1, 0, 1, 3); if (mModes.count() == 0) { mCurrentLabel->setText(i18n("No available resolutions")); } else if (mModes.count() == 1) { mCurrentLabel->setText(Utils::sizeToString(mModes.first())); } else { mSlider = new QSlider(Qt::Horizontal, this); mSlider->setTickInterval(1); mSlider->setTickPosition(QSlider::TicksBelow); mSlider->setSingleStep(1); mSlider->setPageStep(1); mSlider->setMinimum(0); mSlider->setMaximum(mModes.size() - 1); mSlider->setSingleStep(1); if (output->currentMode()) { mSlider->setValue(mModes.indexOf(output->currentMode()->size())); } else if (output->preferredMode()) { mSlider->setValue(mModes.indexOf(output->preferredMode()->size())); } else { mSlider->setValue(mSlider->maximum()); } layout->addWidget(mSlider, 0, 1); connect(mSlider, &QSlider::valueChanged, this, &ResolutionSlider::slotValueChanged); mSmallestLabel = new QLabel(this); mSmallestLabel->setText(Utils::sizeToString(mModes.first())); layout->addWidget(mSmallestLabel, 0, 0); mBiggestLabel = new QLabel(this); mBiggestLabel->setText(Utils::sizeToString(mModes.last())); layout->addWidget(mBiggestLabel, 0, 2); mCurrentLabel->setText(Utils::sizeToString(mModes.at(mSlider->value()))); } } } ResolutionSlider::~ResolutionSlider() { } QSize ResolutionSlider::currentResolution() const { if (mModes.isEmpty()) { return QSize(); } if (mModes.size() < 2) { return mModes.first(); } if (mSlider) { return mModes.at(mSlider->value()); } else { const int i = mComboBox->currentIndex(); return i > -1 ? mModes.at(i) : QSize(); } } void ResolutionSlider::slotOutputModeChanged() { if (!mOutput->currentMode()) { return; } if (mSlider) { mSlider->blockSignals(true); mSlider->setValue(mModes.indexOf(mOutput->currentMode()->size())); mSlider->blockSignals(false); } else if (mComboBox) { mComboBox->blockSignals(true); mComboBox->setCurrentIndex(mModes.indexOf(mOutput->currentMode()->size())); mComboBox->blockSignals(false); } } void ResolutionSlider::slotValueChanged(int value) { const QSize &size = mModes.at(value); if (mCurrentLabel) { mCurrentLabel->setText(Utils::sizeToString(size)); } Q_EMIT resolutionChanged(size); } diff --git a/kcm/src/resolutionslider.h b/kcm/src/resolutionslider.h index 0224416..403314d 100644 --- a/kcm/src/resolutionslider.h +++ b/kcm/src/resolutionslider.h @@ -1,63 +1,63 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef RESOLUTIONSLIDER_H #define RESOLUTIONSLIDER_H #include #include #include class QSlider; class QLabel; class QComboBox; class ResolutionSlider : public QWidget { Q_OBJECT public: - explicit ResolutionSlider(const KScreen::OutputPtr &output, QWidget *parent = 0); + explicit ResolutionSlider(const KScreen::OutputPtr &output, QWidget *parent = nullptr); virtual ~ResolutionSlider(); QSize currentResolution() const; Q_SIGNALS: void resolutionChanged(const QSize &size); private Q_SLOTS: void slotValueChanged(int); void slotOutputModeChanged(); private: KScreen::OutputPtr mOutput; QList mModes; - QLabel *mSmallestLabel; - QLabel *mBiggestLabel; - QLabel *mCurrentLabel; - QSlider *mSlider; - QComboBox *mComboBox; + QLabel *mSmallestLabel = nullptr; + QLabel *mBiggestLabel = nullptr; + QLabel *mCurrentLabel = nullptr; + QSlider *mSlider = nullptr; + QComboBox *mComboBox = nullptr; }; #endif // RESOLUTIONSLIDER_H diff --git a/kcm/src/scaling.ui b/kcm/src/scaling.ui index 185a74f..90baf84 100644 --- a/kcm/src/scaling.ui +++ b/kcm/src/scaling.ui @@ -1,155 +1,149 @@ Scaling 0 0 422 185 - - Dialog - Screen Scaling Scale: 10 30 1 5 10 10 true Qt::Horizontal QSlider::TicksBelow 10 - - TextLabel - Qt::Vertical 20 40 QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMessageWidget QFrame
kmessagewidget.h
1
PreviewWidget QWidget
previewwidget.h
1
buttonBox accepted() Scaling accept() 269 164 229 143 buttonBox rejected() Scaling reject() 405 164 421 155
diff --git a/kcm/src/scalingconfig.cpp b/kcm/src/scalingconfig.cpp index 4606444..fb029d8 100644 --- a/kcm/src/scalingconfig.cpp +++ b/kcm/src/scalingconfig.cpp @@ -1,131 +1,131 @@ /* * Copyright (C) 2015 David Edmundson * * 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 "scalingconfig.h" #include #include #include #include #include //we want a scale between 1 and 3.0 in intervals of 0.1 //slider can only handle ints so goes 10-30 #define SLIDER_RATIO 10.0 ScalingConfig::ScalingConfig(const KScreen::OutputList &outputList, QWidget* parent): QDialog(parent), m_outputList(outputList) { ui.setupUi(this); ui.warningWidget->setText(i18n("Scaling changes will come into effect after restart")); ui.warningWidget->show(); connect(ui.scaleSlider, &QSlider::valueChanged, ui.previewWidget, [this](qreal value) { ui.previewWidget->setScale(value / SLIDER_RATIO); }); connect(ui.scaleSlider, &QSlider::valueChanged, ui.scaleLabel, [this](qreal value) { ui.scaleLabel->setText(QString::number(value / SLIDER_RATIO)); }); ui.previewWidget->setScale(1); ui.scaleLabel->setText(QString::number(1)); load(); } ScalingConfig::~ScalingConfig() { } void ScalingConfig::load() { //we load UI from a config, as rdb value might not be updated yet auto config = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); const qreal dpi = config->group("KScreen").readEntry("ScaleFactor", 1.0); m_initialScalingFactor = dpi; ui.scaleSlider->setValue(dpi * SLIDER_RATIO); } void ScalingConfig::accept() { if (scaleFactor() == m_initialScalingFactor) { QDialog::accept(); return; } const qreal scalingFactor = scaleFactor(); //save to config auto config = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); config->group("KScreen").writeEntry("ScaleFactor", scalingFactor); //write env var to be used by startkde.sh to populate the QT_SCREEN_SCALE_FACTORS env var //we use QT_SCREEN_SCALE_FACTORS as opposed to QT_SCALE_FACTOR as we need to use one that will *NOT* scale fonts according to the scale //scaling the fonts makes sense if you don't also set a font DPI, but we *need* to set a font DPI for both PlasmaShell which does it's own thing, and for KDE4/GTK2 applications QString screenFactors; foreach (const KScreen::OutputPtr &output, m_outputList) { - screenFactors.append(output->name() + '=' + QString::number(scalingFactor) + ';'); + screenFactors.append(output->name() + QLatin1Char('=') + QString::number(scalingFactor) + QLatin1Char(';')); } config->group("KScreen").writeEntry("ScreenScaleFactors", screenFactors); KConfig fontConfig("kcmfonts"); auto fontConfigGroup = fontConfig.group("General"); if (qFuzzyCompare(scalingFactor, 1.0)) { //if dpi is the default (96) remove the entry rather than setting it QProcess proc; proc.start(QStringLiteral("xrdb -quiet -remove -nocpp")); if (proc.waitForStarted()) { proc.write(QByteArray("Xft.dpi\n")); proc.closeWriteChannel(); proc.waitForFinished(); } fontConfigGroup.writeEntry("forceFontDPI", 0); } else { QProcess proc; proc.start(QStringLiteral("xrdb -quiet -merge -nocpp")); if (proc.waitForStarted()) { proc.write(QByteArray("Xft.dpi: " + QString::number(scaleDPI()).toLatin1())); proc.closeWriteChannel(); proc.waitForFinished(); } fontConfigGroup.writeEntry("forceFontDPI", scaleDPI()); } QDialog::accept(); } -qreal ScalingConfig::scaleDPI() const +int ScalingConfig::scaleDPI() const { - return scaleFactor() * 96.0; + return qRound(scaleFactor() * 96.0); } qreal ScalingConfig::scaleFactor() const { return ui.scaleSlider->value() / SLIDER_RATIO; } diff --git a/kcm/src/scalingconfig.h b/kcm/src/scalingconfig.h index 5a7100c..11dc4a9 100644 --- a/kcm/src/scalingconfig.h +++ b/kcm/src/scalingconfig.h @@ -1,47 +1,47 @@ /* * Copyright (C) 2015 David Edmundson * * 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 * */ #ifndef SCALINGCONFIG_H #define SCALINGCONFIG_H #include #include #include "ui_scaling.h" class ScalingConfig : public QDialog { Q_OBJECT public: explicit ScalingConfig(const KScreen::OutputList &outputList, QWidget* parent = 0); virtual ~ScalingConfig(); protected: void accept() Q_DECL_OVERRIDE; private: void load(); qreal scaleFactor() const; - qreal scaleDPI() const; + int scaleDPI() const; Ui::Scaling ui; qreal m_initialScalingFactor = 1.0; KScreen::OutputList m_outputList; }; #endif // SCALINGCONFIG_H diff --git a/kcm/src/unifiedoutputconfig.cpp b/kcm/src/unifiedoutputconfig.cpp index 6d77ba4..943fde7 100644 --- a/kcm/src/unifiedoutputconfig.cpp +++ b/kcm/src/unifiedoutputconfig.cpp @@ -1,196 +1,196 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "unifiedoutputconfig.h" #include "collapsablebutton.h" #include "resolutionslider.h" #include "utils.h" -#include "debug.h" +#include "kcm_screen_debug.h" #include #include #include #include #include #include #include #include #include #include bool operator<(const QSize &s1, const QSize &s2) { return s1.width() * s1.height() < s2.width() * s2.height(); } template<> bool qMapLessThanKey(const QSize &s1, const QSize &s2) { return s1 < s2; } UnifiedOutputConfig::UnifiedOutputConfig(const KScreen::ConfigPtr &config, QWidget *parent) : OutputConfig(parent) , mConfig(config) { } UnifiedOutputConfig::~UnifiedOutputConfig() { } void UnifiedOutputConfig::setOutput(const KScreen::OutputPtr &output) { mOutput = output; mClones.clear(); Q_FOREACH (int id, mOutput->clones()) { mClones << mConfig->output(id); } mClones << mOutput; OutputConfig::setOutput(output); } void UnifiedOutputConfig::initUi() { QVBoxLayout *vbox = new QVBoxLayout(this); mTitle = new QLabel(this); mTitle->setAlignment(Qt::AlignHCenter); vbox->addWidget(mTitle); setTitle(i18n("Unified Outputs")); QGridLayout *formLayout = new QGridLayout(); vbox->addLayout(formLayout); vbox->addStretch(2); KScreen::OutputPtr fakeOutput = createFakeOutput(); mResolution = new ResolutionSlider(fakeOutput, this); connect(mResolution, &ResolutionSlider::resolutionChanged, this, &UnifiedOutputConfig::slotResolutionChanged); formLayout->addWidget(new QLabel(i18n("Resolution:"), this), 1, 0); formLayout->addWidget(mResolution, 1, 1); slotResolutionChanged(mResolution->currentResolution()); mRotation = new QComboBox(this); connect(mRotation, static_cast(&QComboBox::currentIndexChanged), this, &UnifiedOutputConfig::slotRotationChanged); mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-up")), i18n("Normal"), KScreen::Output::None); - mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("90° Clockwise"), KScreen::Output::Left); + mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-right")), i18n("90° Clockwise"), KScreen::Output::Right); mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-down")), i18n("Upside Down"), KScreen::Output::Inverted); - mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-right")), i18n("90° Counterclockwise"), KScreen::Output::Right); + mRotation->addItem(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("90° Counterclockwise"), KScreen::Output::Left); formLayout->addWidget(new QLabel(i18n("Orientation:"), this), 2, 0); formLayout->addWidget(mRotation, 2, 1); formLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 2, 3, 1); } KScreen::OutputPtr UnifiedOutputConfig::createFakeOutput() { // Find set of common resolutions QMap commonSizes; Q_FOREACH (const KScreen::OutputPtr &clone, mClones) { QList processedSizes; Q_FOREACH (const KScreen::ModePtr &mode, clone->modes()) { // Make sure we don't count some modes multiple times because of different // refresh rates if (processedSizes.contains(mode->size())) { continue; } processedSizes << mode->size(); if (commonSizes.contains(mode->size())) { commonSizes[mode->size()]++; } else { commonSizes.insert(mode->size(), 1); } } } KScreen::OutputPtr fakeOutput(new KScreen::Output); // This will give us list of resolution that are shared by all outputs QList commonResults = commonSizes.keys(mClones.count()); // If there are no common resolution, fallback to smallest preferred mode if (commonResults.isEmpty()) { QSize smallestMode; Q_FOREACH (const KScreen::OutputPtr &clone, mClones) { qCDebug(KSCREEN_KCM) << smallestMode << clone->preferredMode()->size(); if (!smallestMode.isValid() || clone->preferredMode()->size() < smallestMode) { smallestMode = clone->preferredMode()->size(); } } commonResults << smallestMode; } qSort(commonResults); KScreen::ModeList modes; Q_FOREACH (const QSize &size, commonResults) { KScreen::ModePtr mode(new KScreen::Mode); mode->setSize(size); mode->setId(Utils::sizeToString(size)); mode->setName(mode->id()); modes.insert(mode->id(), mode); } fakeOutput->setModes(modes); fakeOutput->setCurrentModeId(Utils::sizeToString(commonResults.last())); return fakeOutput; } void UnifiedOutputConfig::slotResolutionChanged(const QSize &size) { // Ignore disconnected outputs if (!size.isValid()) { return; } Q_FOREACH (const KScreen::OutputPtr &clone, mClones) { const QString &id = findBestMode(clone, size); if (id.isEmpty()) { // FIXME: Error? return; } clone->setCurrentModeId(id); } Q_EMIT changed(); } QString UnifiedOutputConfig::findBestMode(const KScreen::OutputPtr &output, const QSize &size) { float refreshRate = 0; QString id; Q_FOREACH (const KScreen::ModePtr &mode, output->modes()) { if (mode->size() == size && mode->refreshRate() > refreshRate) { refreshRate = mode->refreshRate(); id = mode->id(); } } return id; } diff --git a/kcm/src/unifiedoutputconfig.h b/kcm/src/unifiedoutputconfig.h index 07e5eb1..d77bf5c 100644 --- a/kcm/src/unifiedoutputconfig.h +++ b/kcm/src/unifiedoutputconfig.h @@ -1,55 +1,55 @@ /* * Copyright 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef UNIFIEDOUTPUTCONFIG_H #define UNIFIEDOUTPUTCONFIG_H #include "outputconfig.h" namespace KScreen { class Output; class Config; } class UnifiedOutputConfig : public OutputConfig { Q_OBJECT public: explicit UnifiedOutputConfig(const KScreen::ConfigPtr &config, QWidget *parent); virtual ~UnifiedOutputConfig(); - void setOutput(const KScreen::OutputPtr &output); + void setOutput(const KScreen::OutputPtr &output) Q_DECL_OVERRIDE; private Q_SLOTS: void slotResolutionChanged(const QSize &size); private: - virtual void initUi(); + void initUi() Q_DECL_OVERRIDE; KScreen::OutputPtr createFakeOutput(); QString findBestMode(const KScreen::OutputPtr &output, const QSize &size); private: KScreen::ConfigPtr mConfig; QList mClones; }; #endif // UNIFIEDOUTPUTCONFIG_H diff --git a/kcm/src/widget.cpp b/kcm/src/widget.cpp index 16be309..7daf39f 100644 --- a/kcm/src/widget.cpp +++ b/kcm/src/widget.cpp @@ -1,479 +1,485 @@ /* * Copyright (C) 2013 Daniel Vr??til * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) 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 "widget.h" #include "controlpanel.h" #ifdef WITH_PROFILES #include "profilesmodel.h" #endif #include "primaryoutputcombo.h" #include #include #include #include #include #include "declarative/qmloutput.h" #include "declarative/qmlscreen.h" #include "utils.h" #include "scalingconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #define QML_PATH "kcm_kscreen/qml/" Widget::Widget(QWidget *parent): QWidget(parent), - mScreen(0), - mConfig(0), - mPrevConfig(0) + mScreen(nullptr), + mConfig(nullptr), + mPrevConfig(nullptr) { qRegisterMetaType(); setMinimumHeight(550); QVBoxLayout *layout = new QVBoxLayout(this); QSplitter *splitter = new QSplitter(Qt::Vertical, this); layout->addWidget(splitter); - mDeclarativeView = new QQuickView(); - QWidget *container = QWidget::createWindowContainer(mDeclarativeView, this); - mDeclarativeView->setResizeMode(QQuickView::SizeRootObjectToView); + mDeclarativeView = new QQuickWidget(); + mDeclarativeView->setResizeMode(QQuickWidget::SizeRootObjectToView); mDeclarativeView->setMinimumHeight(280); - container->setMinimumHeight(280); - splitter->addWidget(container); + splitter->addWidget(mDeclarativeView); QWidget *widget = new QWidget(this); splitter->addWidget(widget); splitter->setStretchFactor(1, 1); widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); QVBoxLayout *vbox = new QVBoxLayout(widget); + const int topMargin = style()->pixelMetric(QStyle::PM_LayoutTopMargin, 0, this); + vbox->setContentsMargins(0, topMargin, 0, 0); widget->setLayout(vbox); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); mPrimaryCombo = new PrimaryOutputCombo(this); connect(mPrimaryCombo, &PrimaryOutputCombo::changed, this, &Widget::changed); - hbox->addWidget(new QLabel(i18n("Primary display:"))); + mPrimaryLabel = new QLabel(i18n("Primary display:")); + hbox->addWidget(mPrimaryLabel); hbox->addWidget(mPrimaryCombo); hbox->addStretch(); #ifdef WITH_PROFILES mProfilesModel = new ProfilesModel(this); connect(mProfilesModel, &ProfilesModel::modelUpdated()), this, &Widget::slotProfilesUpdated); mProfilesCombo = new QComboBox(this); mProfilesCombo->setModel(mProfilesModel); mProfilesCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); hbox->addWidget(new QLabel(i18n("Active profile"))); hbox->addWidget(mProfilesCombo); #endif mControlPanel = new ControlPanel(this); connect(mControlPanel, &ControlPanel::changed, this, &Widget::changed); vbox->addWidget(mControlPanel); mUnifyButton = new QPushButton(i18n("Unify Outputs"), this); connect(mUnifyButton, &QPushButton::released, [this]{ slotUnifyOutputs(); }); vbox->addWidget(mUnifyButton); - auto setScaleButton = new QPushButton(i18n("Scale Display"), this); - connect(setScaleButton, &QPushButton::released, + mScaleAllOutputsButton = new QPushButton(i18n("Scale Display"), this); + connect(mScaleAllOutputsButton, &QPushButton::released, [this] { QPointer dialog = new ScalingConfig(mConfig->outputs(), this); dialog->exec(); delete dialog; }); - - vbox->addWidget(setScaleButton); + + vbox->addWidget(mScaleAllOutputsButton); + mOutputTimer = new QTimer(this); connect(mOutputTimer, &QTimer::timeout, this, &Widget::clearOutputIdentifiers); loadQml(); } Widget::~Widget() { clearOutputIdentifiers(); } bool Widget::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Resize) { if (mOutputIdentifiers.contains(qobject_cast(object))) { QResizeEvent *e = static_cast(event); const QRect screenSize = object->property("screenSize").toRect(); QRect geometry(QPoint(0, 0), e->size()); geometry.moveCenter(screenSize.center()); static_cast(object)->setGeometry(geometry); // Pass the event further } } return QObject::eventFilter(object, event); } void Widget::setConfig(const KScreen::ConfigPtr &config) { if (mConfig) { KScreen::ConfigMonitor::instance()->removeConfig(mConfig); for (const KScreen::OutputPtr &output : mConfig->outputs()) { output->disconnect(this); } } mConfig = config; KScreen::ConfigMonitor::instance()->addConfig(mConfig); mScreen->setConfig(mConfig); mControlPanel->setConfig(mConfig); mPrimaryCombo->setConfig(mConfig); mUnifyButton->setEnabled(mConfig->outputs().count() > 1); + mScaleAllOutputsButton->setVisible(!mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); + mPrimaryCombo->setVisible(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); + mPrimaryLabel->setVisible(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); for (const KScreen::OutputPtr &output : mConfig->outputs()) { connect(output.data(), &KScreen::Output::isEnabledChanged, this, &Widget::slotOutputEnabledChanged); connect(output.data(), &KScreen::Output::posChanged, this, &Widget::changed); } // Select the primary (or only) output by default QMLOutput *qmlOutput = mScreen->primaryOutput(); if (qmlOutput) { mScreen->setActiveOutput(qmlOutput); } else { - if (mScreen->outputs().count() > 0) { + if (!mScreen->outputs().isEmpty()) { mScreen->setActiveOutput(mScreen->outputs()[0]); } } slotOutputEnabledChanged(); } KScreen::ConfigPtr Widget::currentConfig() const { return mConfig; } void Widget::loadQml() { qmlRegisterType("org.kde.kscreen", 1, 0, "QMLOutput"); qmlRegisterType("org.kde.kscreen", 1, 0, "QMLScreen"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenOutput"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenEdid"); qmlRegisterType("org.kde.kscreen", 1, 0, "KScreenMode"); //const QString file = QDir::currentPath() + "/main.qml"; const QString file = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kcm_kscreen/qml/main.qml")); mDeclarativeView->setSource(QUrl::fromLocalFile(file)); QQuickItem* rootObject = mDeclarativeView->rootObject(); mScreen = rootObject->findChild(QStringLiteral("outputView")); if (!mScreen) { return; } mScreen->setEngine(mDeclarativeView->engine()); connect(mScreen, &QMLScreen::focusedOutputChanged, this, &Widget::slotFocusedOutputChanged); connect(rootObject->findChild(QStringLiteral("identifyButton")), SIGNAL(clicked()), this, SLOT(slotIdentifyButtonClicked())); } void Widget::slotFocusedOutputChanged(QMLOutput *output) { mControlPanel->activateOutput(output->outputPtr()); } void Widget::slotOutputEnabledChanged() { int enabledOutputsCnt = 0; Q_FOREACH (const KScreen::OutputPtr &output, mConfig->outputs()) { if (output->isEnabled()) { ++enabledOutputsCnt; } if (enabledOutputsCnt > 1) { break; } } mUnifyButton->setEnabled(enabledOutputsCnt > 1); } void Widget::slotUnifyOutputs() { QMLOutput *base = mScreen->primaryOutput(); QList clones; if (!base) { Q_FOREACH (QMLOutput *output, mScreen->outputs()) { if (output->output()->isConnected() && output->output()->isEnabled()) { base = output; break; } } if (!base) { // WTF? return; } } if (base->isCloneMode()) { setConfig(mPrevConfig); mPrevConfig.clear(); mPrimaryCombo->setEnabled(true); mUnifyButton->setText(i18n("Unify Outputs")); } else { // Clone the current config, so that we can restore it in case user // breaks the cloning mPrevConfig = mConfig->clone(); Q_FOREACH (QMLOutput *output, mScreen->outputs()) { if (!output->output()->isConnected()) { continue; } if (!output->output()->isEnabled()) { output->setVisible(false); continue; } if (base == 0) { base = output; } output->setOutputX(0); output->setOutputY(0); output->output()->setPos(QPoint(0, 0)); output->output()->setClones(QList()); if (base != output) { clones << output->output()->id(); output->setCloneOf(base); output->setVisible(false); } } base->output()->setClones(clones); base->setIsCloneMode(true); mScreen->updateOutputsPlacement(); mPrimaryCombo->setEnabled(false); mControlPanel->setUnifiedOutput(base->outputPtr()); mUnifyButton->setText(i18n("Break Unified Outputs")); } Q_EMIT changed(); } void Widget::slotProfileChanged(int index) { #ifdef WITH_PROFILES const QVariantMap profile = mProfilesCombo->itemData(index, ProfilesModel::ProfileRole).toMap(); const QVariantList outputs = profile[QLatin1String("outputs")].toList(); // FIXME: Copy-pasted from KDED's Serializer::config() KScreen::Config *config = KScreen::Config::current(); KScreen::OutputList outputList = config->outputs(); Q_FOREACH(KScreen::Output * output, outputList) { if (!output->isConnected() && output->isEnabled()) { output->setEnabled(false); } } KScreen::Config *outputsConfig = config->clone(); Q_FOREACH(const QVariant & info, outputs) { KScreen::Output *output = findOutput(outputsConfig, info.toMap()); if (!output) { continue; } delete outputList.take(output->id()); outputList.insert(output->id(), output); } config->setOutputs(outputList); setConfig(config); #else Q_UNUSED(index) #endif } // FIXME: Copy-pasted from KDED's Serializer::findOutput() KScreen::OutputPtr Widget::findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info) { KScreen::OutputList outputs = config->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } const QString outputId = (output->edid() && output->edid()->isValid()) ? output->edid()->hash() : output->name(); if (outputId != info[QStringLiteral("id")].toString()) { continue; } QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); output->setPos(point); output->setPrimary(info[QStringLiteral("primary")].toBool()); output->setEnabled(info[QStringLiteral("enabled")].toBool()); output->setRotation(static_cast(info[QStringLiteral("rotation")].toInt())); QVariantMap modeInfo = info[QStringLiteral("mode")].toMap(); QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); QSize size(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt()); const KScreen::ModeList modes = output->modes(); Q_FOREACH(const KScreen::ModePtr &mode, modes) { if (mode->size() != size) { continue; } if (QString::number(mode->refreshRate()) != modeInfo[QStringLiteral("refresh")].toString()) { continue; } output->setCurrentModeId(mode->id()); break; } return output; } return KScreen::OutputPtr(); } void Widget::slotProfilesAboutToUpdate() { #ifdef WITH_PROFILES disconnect(mProfilesCombo, &QComboBox::currentIndexChanged, this, &Widget::slotProfileChanged); #endif } void Widget::slotProfilesUpdated() { #ifdef WITH_PROFILES connect(mProfilesCombo, &QComboBox::currentIndexChanged, this, &Widget::slotProfileChanged); const int index = mProfilesModel->activeProfileIndex(); mProfilesCombo->setCurrentIndex(index); #endif } void Widget::clearOutputIdentifiers() { mOutputTimer->stop(); qDeleteAll(mOutputIdentifiers); mOutputIdentifiers.clear(); } void Widget::slotIdentifyButtonClicked(bool checked) { Q_UNUSED(checked); connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, &Widget::slotIdentifyOutputs); } void Widget::slotIdentifyOutputs(KScreen::ConfigOperation *op) { if (op->hasError()) { return; } const KScreen::ConfigPtr config = qobject_cast(op)->config(); const QString qmlPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(QML_PATH "OutputIdentifier.qml")); mOutputTimer->stop(); clearOutputIdentifiers(); /* Obtain the current active configuration from KScreen */ Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (!output->isConnected() || !output->currentMode()) { continue; } const KScreen::ModePtr mode = output->currentMode(); QQuickView *view = new QQuickView(); view->setFlags(Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); view->setResizeMode(QQuickView::SizeViewToRootObject); view->setSource(QUrl::fromLocalFile(qmlPath)); view->installEventFilter(this); QQuickItem *rootObj = view->rootObject(); if (!rootObj) { qWarning() << "Failed to obtain root item"; continue; } QSize realSize; if (output->isHorizontal()) { realSize = mode->size(); } else { realSize = QSize(mode->size().height(), mode->size().width()); } rootObj->setProperty("outputName", Utils::outputName(output)); rootObj->setProperty("modeName", Utils::sizeToString(realSize)); view->setProperty("screenSize", QRect(output->pos(), realSize)); mOutputIdentifiers << view; } Q_FOREACH (QQuickView *view, mOutputIdentifiers) { view->show(); } mOutputTimer->start(2500); } diff --git a/kcm/src/widget.h b/kcm/src/widget.h index aed76a5..dfc9f6a 100644 --- a/kcm/src/widget.h +++ b/kcm/src/widget.h @@ -1,102 +1,106 @@ /* * Copyright (C) 2013 Daniel Vrátil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) 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. * */ #ifndef WIDGET_H #define WIDGET_H #include #include #include class ProfilesModel; +class QLabel; class QMLOutput; class QMLScreen; class ControlPanel; class PrimaryOutputCombo; class QPushButton; class QComboBox; class QQuickView; +class QQuickWidget; namespace KScreen { class ConfigOperation; } class Widget : public QWidget { Q_OBJECT public: - explicit Widget(QWidget *parent = 0); + explicit Widget(QWidget *parent = nullptr); virtual ~Widget(); void setConfig(const KScreen::ConfigPtr &config); KScreen::ConfigPtr currentConfig() const; protected: - virtual bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE; Q_SIGNALS: void changed(); private Q_SLOTS: void slotFocusedOutputChanged(QMLOutput *output); void slotOutputEnabledChanged(); void slotUnifyOutputs(); void slotProfileChanged(int index); void slotProfilesAboutToUpdate(); void slotProfilesUpdated(); void slotIdentifyButtonClicked(bool checked = true); void slotIdentifyOutputs(KScreen::ConfigOperation *op); void clearOutputIdentifiers(); private: void loadQml(); void initPrimaryCombo(); KScreen::OutputPtr findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info); private: - QMLScreen *mScreen; + QMLScreen *mScreen = nullptr; KScreen::ConfigPtr mConfig; KScreen::ConfigPtr mPrevConfig; - QQuickView *mDeclarativeView; - ControlPanel *mControlPanel; + QQuickWidget *mDeclarativeView = nullptr; + ControlPanel *mControlPanel = nullptr; - ProfilesModel *mProfilesModel; - PrimaryOutputCombo *mPrimaryCombo; - QComboBox *mProfilesCombo; + ProfilesModel *mProfilesModel = nullptr; + PrimaryOutputCombo *mPrimaryCombo = nullptr; + QLabel *mPrimaryLabel = nullptr; + QComboBox *mProfilesCombo = nullptr; - QPushButton *mUnifyButton; - QPushButton *mSaveProfileButton; + QPushButton *mScaleAllOutputsButton = nullptr; + QPushButton *mUnifyButton = nullptr; + QPushButton *mSaveProfileButton = nullptr; QList mOutputIdentifiers; - QTimer *mOutputTimer; + QTimer *mOutputTimer = nullptr; }; #endif // WIDGET_H diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt index 08b70f3..5f20388 100644 --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,32 +1,53 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kscreen\") -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../ ${CMAKE_SOURCE_DIR}/kcm/src) set(kscreen_daemon_SRCS - debug.cpp daemon.cpp serializer.cpp generator.cpp device.cpp + osd.cpp + osdmanager.cpp + ${CMAKE_SOURCE_DIR}/kcm/src/utils.cpp ) +ecm_qt_declare_logging_category(kscreen_daemon_SRCS HEADER kscreen_daemon_debug.h IDENTIFIER KSCREEN_KDED CATEGORY_NAME kscreen.kded) + qt5_add_dbus_interface(kscreen_daemon_SRCS org.freedesktop.DBus.Properties.xml freedesktop_interface) qt5_add_dbus_adaptor(kscreen_daemon_SRCS org.kde.KScreen.xml daemon.h KScreenDaemon ) add_library(kscreen MODULE ${kscreen_daemon_SRCS}) -target_link_libraries(kscreen Qt5::Widgets Qt5::DBus KF5::Screen KF5::DBusAddons KF5::I18n KF5::XmlGui KF5::GlobalAccel) +target_link_libraries(kscreen Qt5::Widgets + Qt5::DBus + Qt5::Quick + KF5::Declarative + KF5::Screen + KF5::DBusAddons + KF5::I18n + KF5::XmlGui + KF5::GlobalAccel) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kscreen.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/kscreen.desktop @ONLY) kcoreaddons_desktop_to_json(kscreen ${CMAKE_CURRENT_BINARY_DIR}/kscreen.desktop) install(TARGETS kscreen DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kded) + +set(QML_FILES + qml/Osd.qml + qml/OsdItem.qml + qml/OsdSelector.qml + qml/OutputIdentifier.qml +) + +install(FILES ${QML_FILES} DESTINATION ${DATA_INSTALL_DIR}/kded_kscreen/qml) diff --git a/kded/daemon.cpp b/kded/daemon.cpp index 0c6140f..e05592b 100644 --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -1,422 +1,451 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright 2016 by Sebastian Kügler * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "daemon.h" #include "serializer.h" #include "generator.h" #include "device.h" #include "kscreenadaptor.h" -#include "debug.h" +#include "kscreen_daemon_debug.h" +#include "osdmanager.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KScreenDaemonFactory, "kscreen.json", registerPlugin();) KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& ) : KDEDModule(parent) - , m_monitoredConfig(0) - , m_iteration(Generator::None) + , m_monitoredConfig(nullptr) , m_monitoring(false) , m_changeCompressor(new QTimer(this)) - , m_buttonTimer(new QTimer(this)) , m_saveTimer(new QTimer(this)) , m_lidClosedTimer(new QTimer(this)) { KScreen::Log::instance(); QMetaObject::invokeMethod(this, "requestConfig", Qt::QueuedConnection); } void KScreenDaemon::requestConfig() { connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, &KScreenDaemon::configReady); } void KScreenDaemon::configReady(KScreen::ConfigOperation* op) { if (op->hasError()) { return; } m_monitoredConfig = qobject_cast(op)->config(); qCDebug(KSCREEN_KDED) << "Config" << m_monitoredConfig.data() << "is ready"; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); init(); } KScreenDaemon::~KScreenDaemon() { Generator::destroy(); Device::destroy(); } void KScreenDaemon::init() { KActionCollection *coll = new KActionCollection(this); QAction* action = coll->addAction(QStringLiteral("display")); action->setText(i18n("Switch Display" )); QList switchDisplayShortcuts({Qt::Key_Display, Qt::MetaModifier + Qt::Key_P}); KGlobalAccel::self()->setGlobalShortcut(action, switchDisplayShortcuts); connect(action, &QAction::triggered, [&](bool) { displayButton(); }); new KScreenAdaptor(this); - - m_buttonTimer->setInterval(300); - m_buttonTimer->setSingleShot(true); - connect(m_buttonTimer, &QTimer::timeout, this, &KScreenDaemon::applyGenericConfig); + // Initialize OSD manager to register its dbus interface + KScreen::OsdManager::self(); m_saveTimer->setInterval(300); m_saveTimer->setSingleShot(true); connect(m_saveTimer, &QTimer::timeout, this, &KScreenDaemon::saveCurrentConfig); m_changeCompressor->setInterval(10); m_changeCompressor->setSingleShot(true); connect(m_changeCompressor, &QTimer::timeout, this, &KScreenDaemon::applyConfig); m_lidClosedTimer->setInterval(1000); m_lidClosedTimer->setSingleShot(true); connect(m_lidClosedTimer, &QTimer::timeout, this, &KScreenDaemon::lidClosedTimeout); connect(Device::self(), &Device::lidClosedChanged, this, &KScreenDaemon::lidClosedChanged); connect(Device::self(), &Device::resumingFromSuspend, this, [&]() { KScreen::Log::instance()->setContext("resuming"); qCDebug(KSCREEN_KDED) << "Resumed from suspend, checking for screen changes"; // We don't care about the result, we just want to force the backend // to query XRandR so that it will detect possible changes that happened // while the computer was suspended, and will emit the change events. new KScreen::GetConfigOperation(KScreen::GetConfigOperation::NoEDID, this); }); connect(Device::self(), &Device::aboutToSuspend, this, [&]() { qCDebug(KSCREEN_KDED) << "System is going to suspend, won't be changing config (waited for " << (m_lidClosedTimer->interval() - m_lidClosedTimer->remainingTime()) << "ms)"; m_lidClosedTimer->stop(); }); connect(Generator::self(), &Generator::ready, this, &KScreenDaemon::applyConfig); Generator::self()->setCurrentConfig(m_monitoredConfig); monitorConnectedChange(); } void KScreenDaemon::doApplyConfig(const KScreen::ConfigPtr& config) { qCDebug(KSCREEN_KDED) << "doApplyConfig()"; setMonitorForChanges(false); + m_monitoredConfig = config; + KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); connect(new KScreen::SetConfigOperation(config), &KScreen::SetConfigOperation::finished, this, [&]() { qCDebug(KSCREEN_KDED) << "Config applied"; setMonitorForChanges(true); }); } void KScreenDaemon::applyConfig() { qCDebug(KSCREEN_KDED) << "Applying config"; if (Serializer::configExists(m_monitoredConfig)) { applyKnownConfig(); return; } applyIdealConfig(); } void KScreenDaemon::applyKnownConfig() { const QString configId = Serializer::configId(m_monitoredConfig); qCDebug(KSCREEN_KDED) << "Applying known config" << configId; // We may look for a config that has been set when the lid was closed, Bug: 353029 if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { Serializer::moveConfig(configId + QStringLiteral("_lidOpened"), configId); } KScreen::ConfigPtr config = Serializer::config(m_monitoredConfig, configId); // It's possible that the Serializer returned a nullptr if (!config || !KScreen::Config::canBeApplied(config, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen)) { return applyIdealConfig(); } doApplyConfig(config); } +void KScreenDaemon::applyOsdAction(KScreen::OsdAction::Action action) +{ + switch (action) { + case KScreen::OsdAction::NoAction: + qCDebug(KSCREEN_KDED) << "OSD: no action"; + return; + case KScreen::OsdAction::SwitchToInternal: + qCDebug(KSCREEN_KDED) << "OSD: switch to internal"; + doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffExternal)); + return; + case KScreen::OsdAction::SwitchToExternal: + qCDebug(KSCREEN_KDED) << "OSD: switch to external"; + doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffEmbedded)); + return; + case KScreen::OsdAction::ExtendLeft: + qCDebug(KSCREEN_KDED) << "OSD: extend left"; + doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToLeft)); + return; + case KScreen::OsdAction::ExtendRight: + qCDebug(KSCREEN_KDED) << "OSD: extend right"; + doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToRight)); + return; + case KScreen::OsdAction::Clone: + qCDebug(KSCREEN_KDED) << "OSD: clone"; + doApplyConfig(Generator::self()->displaySwitch(Generator::Clone)); + return; + } + + Q_UNREACHABLE(); +} + void KScreenDaemon::applyIdealConfig() { - qCDebug(KSCREEN_KDED) << "Applying ideal config"; - doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); + + if (m_monitoredConfig->connectedOutputs().count() < 2) { + KScreen::OsdManager::self()->hideOsd(); + doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); + } else { + qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; + auto action = KScreen::OsdManager::self()->showActionSelector(); + connect(action, &KScreen::OsdAction::selected, + this, &KScreenDaemon::applyOsdAction); + } } -void logConfig(const KScreen::ConfigPtr config) { +void logConfig(const KScreen::ConfigPtr &config) { if (config) { foreach (auto o, config->outputs()) { if (o->isConnected()) { qCDebug(KSCREEN_KDED) << o; } } } } void KScreenDaemon::configChanged() { qCDebug(KSCREEN_KDED) << "Change detected"; logConfig(m_monitoredConfig); + + // Modes may have changed, fix-up current mode id + Q_FOREACH(const KScreen::OutputPtr &output, m_monitoredConfig->outputs()) { + if (output->isConnected() && output->isEnabled() && output->currentMode().isNull()) { + qCDebug(KSCREEN_KDED) << "Current mode" << output->currentModeId() << "invalid, setting preferred mode" << output->preferredModeId(); + output->setCurrentModeId(output->preferredModeId()); + doApplyConfig(m_monitoredConfig); + } + } + // Reset timer, delay the writeback m_saveTimer->start(); } void KScreenDaemon::saveCurrentConfig() { qCDebug(KSCREEN_KDED) << "Saving current config to file"; // We assume the config is valid, since it's what we got, but we are interested // in the "at least one enabled screen" check + const bool valid = KScreen::Config::canBeApplied(m_monitoredConfig, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); if (valid) { Serializer::saveConfig(m_monitoredConfig, Serializer::configId(m_monitoredConfig)); logConfig(m_monitoredConfig); } else { qCWarning(KSCREEN_KDED) << "Config does not have at least one screen enabled, WILL NOT save this config, this is not what user wants."; logConfig(m_monitoredConfig); } } void KScreenDaemon::showOsd(const QString &icon, const QString &text) { QDBusMessage msg = QDBusMessage::createMethodCall( QLatin1Literal("org.kde.plasmashell"), QLatin1Literal("/org/kde/osdService"), QLatin1Literal("org.kde.osdService"), QLatin1Literal("showText") ); msg << icon << text; QDBusConnection::sessionBus().asyncCall(msg); } -void KScreenDaemon::displayButton() +void KScreenDaemon::showOutputIdentifier() { - qCDebug(KSCREEN_KDED) << "displayBtn triggered"; - - QString message = i18nc("OSD text after XF86Display button press", "No External Display"); - if (m_monitoredConfig && m_monitoredConfig->connectedOutputs().count() > 1) { - message = i18nc("OSD text after XF86Display button press", "Changing Screen Layout"); - } - showOsd(QStringLiteral("preferences-desktop-display-randr"), message); - - if (m_buttonTimer->isActive()) { - qCDebug(KSCREEN_KDED) << "Too fast, cowboy"; - return; - } - - m_buttonTimer->start(); + KScreen::OsdManager::self()->showOutputIdentifiers(); } -void KScreenDaemon::resetDisplaySwitch() -{ - qCDebug(KSCREEN_KDED) << "resetDisplaySwitch()"; - m_iteration = Generator::None; -} - -void KScreenDaemon::applyGenericConfig() +void KScreenDaemon::displayButton() { - if (m_iteration == Generator::ExtendToRight) { - m_iteration = Generator::None; - } - - m_iteration = Generator::DisplaySwitchAction(static_cast(m_iteration) + 1); - qCDebug(KSCREEN_KDED) << "displayButton: " << m_iteration; + qCDebug(KSCREEN_KDED) << "displayBtn triggered"; - doApplyConfig(Generator::self()->displaySwitch(m_iteration)); + auto action = KScreen::OsdManager::self()->showActionSelector(); + connect(action, &KScreen::OsdAction::selected, + this, &KScreenDaemon::applyOsdAction); } void KScreenDaemon::lidClosedChanged(bool lidIsClosed) { // Ignore this when we don't have any external monitors, we can't turn off our // only screen if (m_monitoredConfig->connectedOutputs().count() == 1) { return; } if (lidIsClosed) { // Lid is closed, now we wait for couple seconds to find out whether it // will trigger a suspend (see Device::aboutToSuspend), or whether we should // turn off the screen qCDebug(KSCREEN_KDED) << "Lid closed, waiting to see if the computer goes to sleep..."; m_lidClosedTimer->start(); return; } else { qCDebug(KSCREEN_KDED) << "Lid opened!"; // We should have a config with "_lidOpened" suffix lying around. If not, // then the configuration has changed while the lid was closed and we just // use applyConfig() and see what we can do ... const QString openConfigId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); if (Serializer::configExists(openConfigId)) { const KScreen::ConfigPtr openedConfig = Serializer::config(m_monitoredConfig, openConfigId); Serializer::removeConfig(openConfigId); doApplyConfig(openedConfig); } } } void KScreenDaemon::lidClosedTimeout() { // Make sure nothing has changed in the past second... :-) if (!Device::self()->isLidClosed()) { return; } // If we are here, it means that closing the lid did not result in suspend // action. // FIXME: This could be simply because the suspend took longer than m_lidClosedTimer // timeout. Ideally we need to be able to look into PowerDevil config to see // what's the configured action for lid events, but there's no API to do that // and I'm no parsing PowerDevil's configs... qCDebug(KSCREEN_KDED) << "Lid closed without system going to suspend -> turning off the screen"; for (KScreen::OutputPtr &output : m_monitoredConfig->outputs()) { if (output->type() == KScreen::Output::Panel) { if (output->isConnected() && output->isEnabled()) { // Save the current config with opened lid, just so that we know // how to restore it later const QString configId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); Serializer::saveConfig(m_monitoredConfig, configId); disableOutput(m_monitoredConfig, output); doApplyConfig(m_monitoredConfig); return; } } } } void KScreenDaemon::outputConnectedChanged() { if (!m_changeCompressor->isActive()) { m_changeCompressor->start(); } - resetDisplaySwitch(); - KScreen::Output *output = qobject_cast(sender()); qCDebug(KSCREEN_KDED) << "outputConnectedChanged():" << output->name(); if (output->isConnected()) { Q_EMIT outputConnected(output->name()); if (!Serializer::configExists(m_monitoredConfig)) { Q_EMIT unknownOutputConnected(output->name()); } } } void KScreenDaemon::monitorConnectedChange() { KScreen::OutputList outputs = m_monitoredConfig->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); } connect(m_monitoredConfig.data(), &KScreen::Config::outputAdded, this, [this] (const KScreen::OutputPtr output) { if (output->isConnected()) { m_changeCompressor->start(); } connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); }, Qt::UniqueConnection ); + connect(m_monitoredConfig.data(), &KScreen::Config::outputRemoved, + this, &KScreenDaemon::applyConfig, + static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); } void KScreenDaemon::setMonitorForChanges(bool enabled) { if (m_monitoring == enabled) { return; } qCDebug(KSCREEN_KDED) << "Monitor for changes: " << enabled; m_monitoring = enabled; if (m_monitoring) { connect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged, Qt::UniqueConnection); } else { disconnect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged); } } void KScreenDaemon::disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output) { const QRect geom = output->geometry(); qCDebug(KSCREEN_KDED) << "Laptop geometry:" << geom << output->pos() << (output->currentMode() ? output->currentMode()->size() : QSize()); // Move all outputs right from the @p output to left for (KScreen::OutputPtr &otherOutput : config->outputs()) { if (otherOutput == output || !otherOutput->isConnected() || !otherOutput->isEnabled()) { continue; } QPoint otherPos = otherOutput->pos(); if (otherPos.x() >= geom.right() && otherPos.y() >= geom.top() && otherPos.y() <= geom.bottom()) { otherPos.setX(otherPos.x() - geom.width()); } qCDebug(KSCREEN_KDED) << "Moving" << otherOutput->name() << "from" << otherOutput->pos() << "to" << otherPos; otherOutput->setPos(otherPos); } // Disable the output output->setEnabled(false); } KScreen::OutputPtr KScreenDaemon::findEmbeddedOutput(const KScreen::ConfigPtr &config) { Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (output->type() == KScreen::Output::Panel) { return output; } } return KScreen::OutputPtr(); } - #include "daemon.moc" diff --git a/kded/daemon.h b/kded/daemon.h index ba48e1c..59aefd3 100644 --- a/kded/daemon.h +++ b/kded/daemon.h @@ -1,85 +1,84 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KSCREEN_DAEMON_H #define KSCREEN_DAEMON_H #include #include #include #include "generator.h" +#include "osdmanager.h" class QTimer; namespace KScreen { class ConfigOperation; } class Q_DECL_EXPORT KScreenDaemon : public KDEDModule { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KScreen") public: KScreenDaemon(QObject *parent, const QList&); virtual ~KScreenDaemon(); public Q_SLOTS: virtual void requestConfig(); void configReady(KScreen::ConfigOperation *op); void init(); void applyConfig(); void applyKnownConfig(); void applyIdealConfig(); void configChanged(); void saveCurrentConfig(); void displayButton(); - void resetDisplaySwitch(); - void applyGenericConfig(); void lidClosedChanged(bool lidIsClosed); void lidClosedTimeout(); void setMonitorForChanges(bool enabled); void outputConnectedChanged(); + void showOutputIdentifier(); + void applyOsdAction(KScreen::OsdAction::Action action); Q_SIGNALS: void outputConnected(const QString &outputName); void unknownOutputConnected(const QString &outputName); protected: virtual void doApplyConfig(const KScreen::ConfigPtr &config); void monitorConnectedChange(); static KScreen::OutputPtr findEmbeddedOutput(const KScreen::ConfigPtr &config); void disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output); void showOsd(const QString &icon, const QString &text); KScreen::ConfigPtr m_monitoredConfig; - Generator::DisplaySwitchAction m_iteration; bool m_monitoring; QTimer* m_changeCompressor; - QTimer* m_buttonTimer; QTimer* m_saveTimer; QTimer* m_lidClosedTimer; }; #endif /*KSCREEN_DAEMON_H*/ diff --git a/kded/debug.cpp b/kded/debug.cpp deleted file mode 100644 index cb0eb8b..0000000 --- a/kded/debug.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2014 Daniel Vratil - * - * 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 "debug.h" - -Q_LOGGING_CATEGORY(KSCREEN_KDED, "kscreen.kded") diff --git a/kded/debug.h b/kded/debug.h deleted file mode 100644 index 4a67311..0000000 --- a/kded/debug.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2014 Daniel Vratil - * - * 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 - * - */ - -#ifndef KDED_DEBUG_H -#define KDED_DEBUG_H - -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(KSCREEN_KDED) - -#endif diff --git a/kded/device.cpp b/kded/device.cpp index 71bf2e3..1a975cc 100644 --- a/kded/device.cpp +++ b/kded/device.cpp @@ -1,178 +1,178 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2015 by Daniel Vrátil * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "device.h" -#include "debug.h" +#include "kscreen_daemon_debug.h" #include "kded/freedesktop_interface.h" #include -Device* Device::m_instance = 0; +Device* Device::m_instance = nullptr; Device* Device::self() { if (!Device::m_instance) { m_instance = new Device(); } return m_instance; } void Device::destroy() { delete m_instance; - m_instance = 0; + m_instance = nullptr; } Device::Device(QObject* parent) : QObject(parent) , m_isReady(false) , m_isLaptop(false) , m_isLidClosed(false) , m_isDocked(false) { m_freedesktop = new OrgFreedesktopDBusPropertiesInterface(QStringLiteral("org.freedesktop.UPower"), QStringLiteral("/org/freedesktop/UPower"), QDBusConnection::systemBus(), this); if (!m_freedesktop->isValid()) { qCWarning(KSCREEN_KDED) << "UPower not available, lid detection won't work"; qCDebug(KSCREEN_KDED) << m_freedesktop->lastError().message(); } else { QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.UPower"), QStringLiteral("/org/freedesktop/UPower"), QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"), this, SLOT(changed())); fetchIsLaptop(); } m_suspendSession = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"), QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"), QDBusConnection::sessionBus(), this); if (m_suspendSession->isValid()) { connect(m_suspendSession, SIGNAL(resumingFromSuspend()), this, SIGNAL(resumingFromSuspend())); connect(m_suspendSession, SIGNAL(aboutToSuspend()), this, SIGNAL(aboutToSuspend())); } else { qCWarning(KSCREEN_KDED) << "PowerDevil SuspendSession action not available!"; qCDebug(KSCREEN_KDED) << m_suspendSession->lastError().message(); } fetchIsLaptop(); } Device::~Device() { } void Device::changed() { fetchLidIsClosed(); } void Device::setReady() { if (m_isReady) { return; } m_isReady = true; Q_EMIT ready(); } -bool Device::isReady() +bool Device::isReady() const { return m_isReady; } -bool Device::isLaptop() +bool Device::isLaptop() const { return m_isLaptop; } -bool Device::isLidClosed() +bool Device::isLidClosed() const { return m_isLidClosed; } -bool Device::isDocked() +bool Device::isDocked() const { return m_isDocked; } void Device::fetchIsLaptop() { QDBusPendingReply res = m_freedesktop->Get(QStringLiteral("org.freedesktop.UPower"), QStringLiteral("LidIsPresent")); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Device::isLaptopFetched); } void Device::isLaptopFetched(QDBusPendingCallWatcher* watcher) { const QDBusPendingReply reply = *watcher; if (reply.isError()) { qCDebug(KSCREEN_KDED) << "Couldn't get if the device is a laptop: " << reply.error().message(); return; } m_isLaptop = reply.value().toBool(); watcher->deleteLater(); if (!m_isLaptop) { setReady(); return; } fetchLidIsClosed(); } void Device::fetchLidIsClosed() { QDBusPendingReply res = m_freedesktop->Get(QStringLiteral("org.freedesktop.UPower"), QStringLiteral("LidIsClosed")); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res); connect(watcher, &QDBusPendingCallWatcher::finished, this, &Device::isLidClosedFetched); } void Device::isLidClosedFetched(QDBusPendingCallWatcher* watcher) { const QDBusPendingReply reply = *watcher; if (reply.isError()) { qCDebug(KSCREEN_KDED) << "Couldn't get if the laptop has the lid closed: " << reply.error().message(); return; } if (reply.argumentAt<0>() != m_isLidClosed) { m_isLidClosed = reply.value().toBool(); if (m_isReady) { Q_EMIT lidClosedChanged(m_isLidClosed);; } } watcher->deleteLater(); fetchIsDocked(); } void Device::fetchIsDocked() { setReady(); } diff --git a/kded/device.h b/kded/device.h index 782c246..d6e0b4b 100644 --- a/kded/device.h +++ b/kded/device.h @@ -1,71 +1,71 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KDED_DEVICE_H #define KDED_DEVICE_H #include class QDBusPendingCallWatcher; class QDBusInterface; class OrgFreedesktopDBusPropertiesInterface; class Device : public QObject { Q_OBJECT public: static Device* self(); static void destroy(); - bool isReady(); - bool isLaptop(); - bool isLidClosed(); - bool isDocked(); + bool isReady() const; + bool isLaptop() const; + bool isLidClosed() const; + bool isDocked() const; private Q_SLOTS: void changed(); void isLaptopFetched(QDBusPendingCallWatcher* watcher); void isLidClosedFetched(QDBusPendingCallWatcher* watcher); Q_SIGNALS: void ready(); void lidClosedChanged(bool closed); void resumingFromSuspend(); void aboutToSuspend(); private: - explicit Device(QObject* parent = 0); + explicit Device(QObject* parent = nullptr); virtual ~Device(); void setReady(); void fetchIsLaptop(); void fetchLidIsClosed(); void fetchIsDocked(); bool m_isReady; bool m_isLaptop; bool m_isLidClosed; bool m_isDocked; static Device* m_instance; OrgFreedesktopDBusPropertiesInterface *m_freedesktop; QDBusInterface *m_suspendSession; }; #endif //KDED_DEVICE_H diff --git a/kded/generator.cpp b/kded/generator.cpp index b00fc43..769f8d2 100644 --- a/kded/generator.cpp +++ b/kded/generator.cpp @@ -1,669 +1,679 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "generator.h" #include "device.h" -#include "debug.h" +#include "kscreen_daemon_debug.h" #include #include #include #include +#include #include #if defined(QT_NO_DEBUG) #define ASSERT_OUTPUTS(outputs) #else #define ASSERT_OUTPUTS(outputs) \ while(true) { \ Q_ASSERT(!outputs.isEmpty()); \ Q_FOREACH (const KScreen::OutputPtr &output, outputs) { \ Q_ASSERT(output); \ Q_ASSERT(output->isConnected()); \ } break; \ } #endif -Generator* Generator::instance = 0; +Generator* Generator::instance = nullptr; bool operator<(const QSize &s1, const QSize &s2) { return s1.width() * s1.height() < s2.width() * s2.height(); } Generator* Generator::self() { if (!Generator::instance) { Generator::instance = new Generator(); } return Generator::instance; } Generator::Generator() : QObject() , m_forceLaptop(false) , m_forceLidClosed(false) , m_forceNotLaptop(false) , m_forceDocked(false) { connect(Device::self(), &Device::ready, this, &Generator::ready); } void Generator::destroy() { delete Generator::instance; - Generator::instance = 0; + Generator::instance = nullptr; } Generator::~Generator() { } void Generator::setCurrentConfig(const KScreen::ConfigPtr ¤tConfig) { m_currentConfig = currentConfig; } KScreen::ConfigPtr Generator::idealConfig(const KScreen::ConfigPtr ¤tConfig) { Q_ASSERT(currentConfig); // KDebug::Block idealBlock("Ideal Config"); KScreen::ConfigPtr config = currentConfig->clone(); disableAllDisconnectedOutputs(config->outputs()); KScreen::OutputList connectedOutputs = config->connectedOutputs(); qCDebug(KSCREEN_KDED) << "Connected outputs: " << connectedOutputs.count(); if (connectedOutputs.isEmpty()) { return config; } + //the scale will generally be independent no matter where the output is + //scale will affect geometry, so do this first + if (config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)) { + for(auto output: qAsConst(connectedOutputs)) { + output->setScale(bestScaleForOutput(output)); + } + } + if (connectedOutputs.count() == 1) { singleOutput(connectedOutputs); return config; } if (isLaptop()) { laptop(connectedOutputs); return fallbackIfNeeded(config); } qCDebug(KSCREEN_KDED) << "Extend to Right"; extendToRight(connectedOutputs); return fallbackIfNeeded(config); } KScreen::ConfigPtr Generator::fallbackIfNeeded(const KScreen::ConfigPtr &config) { qCDebug(KSCREEN_KDED) << "fallbackIfNeeded()"; KScreen::ConfigPtr newConfig; //If the ideal config can't be applied, try clonning if (!KScreen::Config::canBeApplied(config)) { if (isLaptop()) { newConfig = displaySwitch(Generator::Clone); // Try to clone at our best } else { newConfig = config; KScreen::OutputList connectedOutputs = config->connectedOutputs(); if (connectedOutputs.isEmpty()) { return config; } connectedOutputs.value(connectedOutputs.keys().first())->setPrimary(true); cloneScreens(connectedOutputs); } } else { newConfig = config; } //If after trying to clone at our best, we fail... return current if (!KScreen::Config::canBeApplied(newConfig)) { qCDebug(KSCREEN_KDED) << "Config cannot be applied"; newConfig = config; } return config; } KScreen::ConfigPtr Generator::displaySwitch(DisplaySwitchAction action) { // KDebug::Block switchBlock("Display Switch"); KScreen::ConfigPtr config = m_currentConfig; Q_ASSERT(config); KScreen::OutputList connectedOutputs = config->connectedOutputs(); + //the scale will generally be independent no matter where the output is + //scale will affect geometry, so do this first + if (config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)) { + for(auto output: qAsConst(connectedOutputs)) { + output->setScale(bestScaleForOutput(output)); + } + } + // There's not much else we can do with only one output if (connectedOutputs.count() < 2) { singleOutput(connectedOutputs); return config; } // We cannot try all possible combinations with two and more outputs if (connectedOutputs.count() > 2) { extendToRight(connectedOutputs); return config; } KScreen::OutputPtr embedded, external; embedded = embeddedOutput(connectedOutputs); // If we don't have an embedded output (desktop with two external screens // for instance), then pretend one of them is embedded if (!embedded) { embedded = connectedOutputs.value(connectedOutputs.keys().first()); } // Just to be sure if (embedded->modes().isEmpty()) { return config; } if (action == Generator::Clone) { qCDebug(KSCREEN_KDED) << "Cloning"; embedded->setPrimary(true); cloneScreens(connectedOutputs); return config; } connectedOutputs.remove(embedded->id()); external = connectedOutputs.value(connectedOutputs.keys().first()); // Just to be sure if (external->modes().isEmpty()) { return config; } switch (action) { case Generator::ExtendToLeft: { qCDebug(KSCREEN_KDED) << "Extend to left"; external->setPos(QPoint(0,0)); external->setEnabled(true); const KScreen::ModePtr extMode = bestModeForOutput(external); Q_ASSERT(extMode); external->setCurrentModeId(extMode->id()); Q_ASSERT(external->currentMode()); // we must have a mode now - const QSize size = external->currentMode()->size(); + const QSize size = external->geometry().size(); embedded->setPos(QPoint(size.width(), 0)); embedded->setEnabled(true); embedded->setPrimary(true); const KScreen::ModePtr embeddedMode = bestModeForOutput(embedded); Q_ASSERT(embeddedMode); embedded->setCurrentModeId(embeddedMode->id()); return config; } case Generator::TurnOffEmbedded: { qCDebug(KSCREEN_KDED) << "Turn off embedded (laptop)"; embedded->setEnabled(false); embedded->setPrimary(false); external->setEnabled(true); external->setPrimary(true); const KScreen::ModePtr extMode = bestModeForOutput(external); Q_ASSERT(extMode); external->setCurrentModeId(extMode->id()); return config; } case Generator::TurnOffExternal: { qCDebug(KSCREEN_KDED) << "Turn off external screen"; embedded->setPos(QPoint(0,0)); embedded->setEnabled(true); embedded->setPrimary(true); const KScreen::ModePtr embeddedMode = bestModeForOutput(embedded); Q_ASSERT(embeddedMode); embedded->setCurrentModeId(embeddedMode->id()); external->setEnabled(false); external->setPrimary(false); return config; } case Generator::ExtendToRight: { qCDebug(KSCREEN_KDED) << "Extend to the right"; embedded->setPos(QPoint(0,0)); embedded->setEnabled(true); embedded->setPrimary(true); const KScreen::ModePtr embeddedMode = bestModeForOutput(embedded); Q_ASSERT(embeddedMode); embedded->setCurrentModeId(embeddedMode->id()); Q_ASSERT(embedded->currentMode()); // we must have a mode now - const QSize size = embedded->currentMode()->size(); + const QSize size = embedded->geometry().size(); external->setPos(QPoint(size.width(), 0)); external->setEnabled(true); external->setPrimary(false); const KScreen::ModePtr extMode = bestModeForOutput(external); Q_ASSERT(extMode); external->setCurrentModeId(extMode->id()); return config; } case Generator::None: // just return config case Generator::Clone: // handled above break; } // switch return config; } uint qHash(const QSize &size) { return size.width() * size.height(); } void Generator::cloneScreens(KScreen::OutputList &connectedOutputs) { ASSERT_OUTPUTS(connectedOutputs); if (connectedOutputs.isEmpty()) { return; } QSet commonSizes; const QSize maxScreenSize = m_currentConfig->screen()->maxSize(); - QList> modes; Q_FOREACH(const KScreen::OutputPtr &output, connectedOutputs) { QSet modeSizes; Q_FOREACH(const KScreen::ModePtr &mode, output->modes()) { const QSize size = mode->size(); if (size.width() > maxScreenSize.width() || size.height() > maxScreenSize.height()) { continue; } modeSizes.insert(mode->size()); } //If we have nothing to compare against if (commonSizes.isEmpty()) { commonSizes = modeSizes; continue; } commonSizes.intersect(modeSizes); } qCDebug(KSCREEN_KDED) << "Common sizes: " << commonSizes; //fallback to biggestMode if no commonSizes have been found if (commonSizes.isEmpty()) { Q_FOREACH(KScreen::OutputPtr output, connectedOutputs) { if (output->modes().isEmpty()) { continue; } output->setEnabled(true); output->setPos(QPoint(0, 0)); const KScreen::ModePtr mode = biggestMode(output->modes()); Q_ASSERT(mode); output->setCurrentModeId(mode->id()); } return; } //At this point, we know we have common sizes, let's get the biggest on QList commonSizeList = commonSizes.toList(); qSort(commonSizeList.begin(), commonSizeList.end()); const QSize biggestSize = commonSizeList.last(); //Finally, look for the mode with biggestSize and biggest refreshRate and set it qCDebug(KSCREEN_KDED) << "Biggest Size: " << biggestSize; KScreen::ModePtr bestMode; Q_FOREACH(KScreen::OutputPtr output, connectedOutputs) { if (output->modes().isEmpty()) { continue; } bestMode = bestModeForSize(output->modes(), biggestSize); Q_ASSERT(bestMode); // we resolved this mode previously, so it better works output->setEnabled(true); output->setPos(QPoint(0, 0)); output->setCurrentModeId(bestMode->id()); } } void Generator::singleOutput(KScreen::OutputList &connectedOutputs) { ASSERT_OUTPUTS(connectedOutputs); if (connectedOutputs.isEmpty()) { return; } KScreen::OutputPtr output = connectedOutputs.take(connectedOutputs.keys().first()); if (output->modes().isEmpty()) { return; } const KScreen::ModePtr bestMode = bestModeForOutput(output); Q_ASSERT(bestMode); output->setCurrentModeId(bestMode->id()); output->setEnabled(true); output->setPrimary(true); output->setPos(QPoint(0,0)); } void Generator::laptop(KScreen::OutputList &connectedOutputs) { ASSERT_OUTPUTS(connectedOutputs) if (connectedOutputs.isEmpty()) { return; } // KDebug::Block laptopBlock("Laptop config"); KScreen::OutputPtr embedded = embeddedOutput(connectedOutputs); /* Apparently older laptops use "VGA-*" as embedded output ID, so embeddedOutput() * will fail, because it looks only for modern "LVDS", "EDP", etc. If we * fail to detect which output is embedded, just use the one with the lowest * ID. It's a wild guess, but I think it's highly probable that it will work. * See bug #318907 for further reference. -- dvratil */ if (!embedded) { QList keys = connectedOutputs.keys(); qSort(keys); embedded = connectedOutputs.value(keys.first()); } connectedOutputs.remove(embedded->id()); if (connectedOutputs.isEmpty() || embedded->modes().isEmpty()) { qCWarning(KSCREEN_KDED) << "No external outputs found, going for singleOutput()"; connectedOutputs.insert(embedded->id(), embedded); return singleOutput(connectedOutputs); } if (isLidClosed() && connectedOutputs.count() == 1) { qCDebug(KSCREEN_KDED) << "With lid closed"; embedded->setEnabled(false); embedded->setPrimary(false); KScreen::OutputPtr external = connectedOutputs.value(connectedOutputs.keys().first()); if (external->modes().isEmpty()) { return; } external->setEnabled(true); external->setPrimary(true); const KScreen::ModePtr bestMode = bestModeForOutput(external); Q_ASSERT(bestMode); external->setCurrentModeId(bestMode->id()); external->setPos(QPoint(0, 0)); return; } if (isLidClosed() && connectedOutputs.count() > 1) { qCDebug(KSCREEN_KDED) << "Lid is closed, and more than one output"; embedded->setEnabled(false); embedded->setPrimary(false); extendToRight(connectedOutputs); return; } qCDebug(KSCREEN_KDED) << "Lid is open"; //If lid is open, laptop screen shuold be primary embedded->setPos(QPoint(0,0)); embedded->setPrimary(true); embedded->setEnabled(true); const KScreen::ModePtr embeddedMode = bestModeForOutput(embedded); Q_ASSERT(embeddedMode); embedded->setCurrentModeId(embeddedMode->id()); - int globalWidth; - if (embedded->isHorizontal()) { - globalWidth = embedded->currentMode()->size().width(); - } else { - globalWidth = embedded->currentMode()->size().height(); - } + int globalWidth = embedded->geometry().width(); KScreen::OutputPtr biggest = biggestOutput(connectedOutputs); Q_ASSERT(biggest); connectedOutputs.remove(biggest->id()); biggest->setPos(QPoint(globalWidth, 0)); biggest->setEnabled(true); biggest->setPrimary(false); const KScreen::ModePtr mode = bestModeForOutput(biggest); biggest->setCurrentModeId(mode->id()); - if (biggest->isHorizontal()) { - globalWidth += biggest->currentMode()->size().width(); - } else { - globalWidth += biggest->currentMode()->size().height(); - } + globalWidth += biggest->geometry().width(); Q_FOREACH(KScreen::OutputPtr output, connectedOutputs) { output->setEnabled(true); output->setPrimary(false); output->setPos(QPoint(globalWidth, 0)); const KScreen::ModePtr mode = bestModeForOutput(output); Q_ASSERT(mode); output->setCurrentModeId(mode->id()); - if (output->isHorizontal()) { - globalWidth += output->currentMode()->size().width(); - } else { - globalWidth += output->currentMode()->size().height(); - } + globalWidth += output->geometry().width(); } if (isDocked()) { qCDebug(KSCREEN_KDED) << "Docked"; embedded->setPrimary(false); biggest->setPrimary(true); } } void Generator::extendToRight(KScreen::OutputList &connectedOutputs) { ASSERT_OUTPUTS(connectedOutputs); if (connectedOutputs.isEmpty()) { return; } qCDebug(KSCREEN_KDED) << "Extending to the right"; KScreen::OutputPtr biggest = biggestOutput(connectedOutputs); Q_ASSERT(biggest); connectedOutputs.remove(biggest->id()); biggest->setEnabled(true); biggest->setPrimary(true); biggest->setPos(QPoint(0,0)); const KScreen::ModePtr mode = bestModeForOutput(biggest); Q_ASSERT(mode); biggest->setCurrentModeId(mode->id()); - int globalWidth; - if (biggest->isHorizontal()) { - globalWidth = biggest->currentMode()->size().width(); - } else { - globalWidth = biggest->currentMode()->size().height(); - } + int globalWidth = biggest->geometry().width(); Q_FOREACH(KScreen::OutputPtr output, connectedOutputs) { output->setEnabled(true); output->setPrimary(false); output->setPos(QPoint(globalWidth, 0)); const KScreen::ModePtr mode = bestModeForOutput(output); Q_ASSERT(mode); output->setCurrentModeId(mode->id()); - if (output->isHorizontal()) { - globalWidth += output->currentMode()->size().width(); - } else { - globalWidth += output->currentMode()->size().height(); - } + globalWidth += output->geometry().width(); } } KScreen::ModePtr Generator::biggestMode(const KScreen::ModeList &modes) { Q_ASSERT(!modes.isEmpty()); int modeArea, biggestArea = 0; KScreen::ModePtr biggestMode; Q_FOREACH(const KScreen::ModePtr &mode, modes) { modeArea = mode->size().width() * mode->size().height(); if (modeArea < biggestArea) { continue; } if (modeArea == biggestArea && mode->refreshRate() < biggestMode->refreshRate()) { continue; } if (modeArea == biggestArea && mode->refreshRate() > biggestMode->refreshRate()) { biggestMode = mode; continue; } biggestArea = modeArea; biggestMode = mode; } return biggestMode; } KScreen::ModePtr Generator::bestModeForSize(const KScreen::ModeList &modes, const QSize &size) { KScreen::ModePtr bestMode; Q_FOREACH(const KScreen::ModePtr &mode, modes) { if (mode->size() != size) { continue; } if (!bestMode) { bestMode = mode; continue; } if (mode->refreshRate() > bestMode->refreshRate()) { bestMode = mode; } } return bestMode; } +qreal Generator::bestScaleForOutput(const KScreen::OutputPtr &output) { + //if we have no physical size, we can't determine the DPI properly. Fallback to scale 1 + if (output->sizeMm().height() <= 0) { + return 1.0; + } + const auto mode = bestModeForOutput(output); + const qreal dpi = mode->size().height() / (output->sizeMm().height() / 25.4); + + //if reported DPI is closer to two times normal DPI, followed by a sanity check of having the sort of vertical resolution + //you'd find in a high res screen + if (dpi > 96 * 1.5 && mode->size().height() >= 1440) { + return 2.0; + } + return 1.0; +} + KScreen::ModePtr Generator::bestModeForOutput(const KScreen::OutputPtr &output) { if (output->preferredMode()) { return output->preferredMode(); } return biggestMode(output->modes()); } KScreen::OutputPtr Generator::biggestOutput(const KScreen::OutputList &outputs) { ASSERT_OUTPUTS(outputs) int area, total = 0; KScreen::OutputPtr biggest; Q_FOREACH(const KScreen::OutputPtr &output, outputs) { const KScreen::ModePtr mode = bestModeForOutput(output); if (!mode) { continue; } area = mode->size().width() * mode->size().height(); if (area <= total) { continue; } total = area; biggest = output; } return biggest; } void Generator::disableAllDisconnectedOutputs(const KScreen::OutputList &outputs) { // KDebug::Block disableBlock("Disabling disconnected screens"); Q_FOREACH(KScreen::OutputPtr output, outputs) { if (!output->isConnected()) { qCDebug(KSCREEN_KDED) << output->name() << " Disabled"; output->setEnabled(false); output->setPrimary(false); } } } KScreen::OutputPtr Generator::embeddedOutput(const KScreen::OutputList &outputs) { Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (output->type() != KScreen::Output::Panel) { continue; } return output; } return KScreen::OutputPtr(); } -bool Generator::isLaptop() +bool Generator::isLaptop() const { if (m_forceLaptop) { return true; } if (m_forceNotLaptop) { return false; } return Device::self()->isLaptop(); } -bool Generator::isLidClosed() +bool Generator::isLidClosed() const { if (m_forceLidClosed) { return true; } if (m_forceNotLaptop) { return false; } return Device::self()->isLidClosed(); } -bool Generator::isDocked() +bool Generator::isDocked() const { if (m_forceDocked) { return true; } return Device::self()->isDocked(); } void Generator::setForceLaptop(bool force) { m_forceLaptop = force; } void Generator::setForceLidClosed(bool force) { m_forceLidClosed = force; } void Generator::setForceDocked(bool force) { m_forceDocked = force; } void Generator::setForceNotLaptop(bool force) { m_forceNotLaptop = force; } diff --git a/kded/generator.h b/kded/generator.h index 36669fd..272153d 100644 --- a/kded/generator.h +++ b/kded/generator.h @@ -1,95 +1,96 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KDED_GENERATOR_H #define KDED_GENERATOR_H #include #include #include #include namespace KScreen { class Config; } class Generator : public QObject { Q_OBJECT public: enum DisplaySwitchAction { None = 0, Clone = 1, ExtendToLeft = 2, TurnOffEmbedded = 3, TurnOffExternal = 4, ExtendToRight = 5, }; static Generator* self(); static void destroy(); void setCurrentConfig(const KScreen::ConfigPtr ¤tConfig); KScreen::ConfigPtr idealConfig(const KScreen::ConfigPtr ¤tConfig); KScreen::ConfigPtr displaySwitch(DisplaySwitchAction iteration); void setForceLaptop(bool force); void setForceLidClosed(bool force); void setForceDocked(bool force); void setForceNotLaptop(bool force); static KScreen::ModePtr biggestMode(const KScreen::ModeList &modes); Q_SIGNALS: void ready(); private: explicit Generator(); virtual ~Generator(); KScreen::ConfigPtr fallbackIfNeeded(const KScreen::ConfigPtr &config); void cloneScreens(KScreen::OutputList &connectedOutputs); void laptop(KScreen::OutputList &connectedOutputs); void singleOutput(KScreen::OutputList &connectedOutputs); void extendToRight(KScreen::OutputList &connectedOutputs); KScreen::ModePtr bestModeForSize(const KScreen::ModeList& modes, const QSize &size); KScreen::ModePtr bestModeForOutput(const KScreen::OutputPtr &output); + qreal bestScaleForOutput(const KScreen::OutputPtr &output); KScreen::OutputPtr biggestOutput(const KScreen::OutputList &connectedOutputs); KScreen::OutputPtr embeddedOutput(const KScreen::OutputList &connectedOutputs); void disableAllDisconnectedOutputs(const KScreen::OutputList &connectedOutputs); - bool isLaptop(); - bool isLidClosed(); - bool isDocked(); + bool isLaptop() const; + bool isLidClosed() const; + bool isDocked() const; bool m_forceLaptop; bool m_forceLidClosed; bool m_forceNotLaptop; bool m_forceDocked; KScreen::ConfigPtr m_currentConfig; static Generator* instance; }; #endif //KDED_GENERATOR_H diff --git a/kded/kscreen.desktop.cmake b/kded/kscreen.desktop.cmake index b059882..dc89dd2 100644 --- a/kded/kscreen.desktop.cmake +++ b/kded/kscreen.desktop.cmake @@ -1,94 +1,100 @@ [Desktop Entry] Name=KScreen 2 Name[ar]=شاشتك 2 Name[ast]=KScreen 2 Name[bs]=KScreen 2 Name[ca]=KScreen 2 Name[ca@valencia]=KScreen 2 Name[cs]=KScreen 2 Name[da]=KScreen 2 Name[de]=KScreen 2 Name[el]=KScreen 2 Name[en_GB]=KScreen 2 Name[es]=KScreen 2 Name[et]=KScreen 2 +Name[eu]=KScreen 2 Name[fi]=KScreen 2 Name[fr]=KScreen 2 Name[gl]=KScreen 2 +Name[he]=KScreen 2 Name[hu]=KScreen 2 +Name[ia]=Kscreen 2 +Name[id]=KScreen 2 Name[it]=KScreen 2 Name[ko]=KScreen 2 Name[lt]=KScreen 2 Name[nb]=KScreen 2 Name[nl]=KScreen 2 Name[nn]=KScreen 2 Name[pa]=KScreen 2 Name[pl]=KEkran 2 Name[pt]=KScreen 2 Name[pt_BR]=KScreen 2 Name[ro]=KScreen 2 Name[ru]=KScreen 2 Name[sk]=KScreen 2 Name[sl]=KScreen 2 Name[sr]=К‑екран 2 Name[sr@ijekavian]=К‑екран 2 Name[sr@ijekavianlatin]=K‑ekran 2 Name[sr@latin]=K‑ekran 2 Name[sv]=Kscreen 2 Name[tr]=KScreen 2 Name[uk]=KScreen 2 Name[x-test]=xxKScreen 2xx Name[zh_CN]=KScreen 2 Name[zh_TW]=KScreen 2 Comment=Screen management Comment[ar]=إدارة الشّاشة -Comment[ast]=Xestión de pantalla Comment[bs]=Upravljanje ekranom Comment[ca]=Gestió de pantalla Comment[ca@valencia]=Gestió de pantalla Comment[cs]=Správa obrazovek Comment[da]=Håndtering af skærme Comment[de]=Bildschirm-Verwaltung Comment[el]=Διαχείριση οθόνης Comment[en_GB]=Screen management Comment[es]=Gestión de la pantalla Comment[et]=Ekraanihaldur +Comment[eu]=Pantaila kudeatzailea Comment[fi]=Näyttöjen hallinta Comment[fr]=Gestion d'écrans Comment[gl]=Xestor de pantalla Comment[he]=ניהול מסכים Comment[hu]=Képernyőkezelés +Comment[ia]=Gestion de schermo +Comment[id]=Pengelolaan layar Comment[it]=Gestione dello schermo Comment[ko]=화면 관리 Comment[lt]=Ekrano tvarkymas Comment[nb]=Skjembehandling Comment[nl]=Schermbeheer Comment[nn]=Skjermhandsaming Comment[pa]=ਸਕਰੀਨ ਪਰਬੰਧ Comment[pl]=Zarządzanie ekranem Comment[pt]=Gestão do ecrã Comment[pt_BR]=Gerenciamento de telas Comment[ro]=Gestiunea ecranelor Comment[ru]=Управление экранами Comment[sk]=Správa obrazoviek Comment[sl]=Upravljanje zaslonov Comment[sr]=Управљање екраном Comment[sr@ijekavian]=Управљање екраном Comment[sr@ijekavianlatin]=Upravljanje ekranom Comment[sr@latin]=Upravljanje ekranom Comment[sv]=Skärmhantering Comment[tr]=Ekran yönetimi Comment[uk]=Керування екраном Comment[x-test]=xxScreen managementxx Comment[zh_CN]=屏幕管理 Comment[zh_TW]=螢幕管理 Type=Service Icon=preferences-system-power-management X-KDE-ServiceTypes=KDEDModule X-KDE-Library=kscreen X-KDE-DBus-ModuleName=kscreen X-KDE-Kded-autoload=true X-KDE-Kded-load-on-demand=false X-KDE-Kded-phase=1 X-KDE-PluginInfo-Version=@KSCREEN_VERSION@ diff --git a/kded/osd.cpp b/kded/osd.cpp new file mode 100644 index 0000000..be45020 --- /dev/null +++ b/kded/osd.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "osd.h" +#include "utils.h" +#include "kscreen_daemon_debug.h" + +#include + +#include +#include +#include +#include + + +using namespace KScreen; + +Osd::Osd(const KScreen::OutputPtr output, QObject *parent) + : QObject(parent) + , m_output(output) +{ + connect(output.data(), &KScreen::Output::isConnectedChanged, + this, &Osd::onOutputAvailabilityChanged); + connect(output.data(), &KScreen::Output::isEnabledChanged, + this, &Osd::onOutputAvailabilityChanged); + connect(output.data(), &KScreen::Output::currentModeIdChanged, + this, &Osd::updatePosition); + connect(output.data(), &KScreen::Output::destroyed, + this, &Osd::hideOsd); +} + +Osd::~Osd() +{ +} + +bool Osd::initOsd() +{ + if (m_osdObject) { + return true; + } + + const QString &osdPath = QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kded_kscreen/qml/Osd.qml")); + if (osdPath.isEmpty()) { + qCWarning(KSCREEN_KDED) << "Failed to find OSD QML file" << osdPath; + return false; + } + + m_osdObject = new KDeclarative::QmlObject(this); + m_osdObject->setSource(QUrl::fromLocalFile(osdPath)); + + if (m_osdObject->status() != QQmlComponent::Ready) { + qCWarning(KSCREEN_KDED) << "Failed to load OSD QML file" << osdPath; + delete m_osdObject; + m_osdObject = nullptr; + return false; + } + + m_timeout = m_osdObject->rootObject()->property("timeout").toInt(); + m_osdTimer = new QTimer(this); + m_osdTimer->setSingleShot(true); + connect(m_osdTimer, &QTimer::timeout, this, &Osd::hideOsd); + return true; + +} + +void Osd::showGenericOsd(const QString &icon, const QString &text) +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = m_output->geometry(); + auto *rootObject = m_osdObject->rootObject(); + rootObject->setProperty("itemSource", QStringLiteral("OsdItem.qml")); + rootObject->setProperty("infoText", text); + rootObject->setProperty("icon", icon); + + showOsd(); +} + +void Osd::showOutputIdentifier(const KScreen::OutputPtr output) +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = output->geometry(); + + auto *rootObject = m_osdObject->rootObject(); + auto mode = output->currentMode(); + QSize realSize = mode->size(); + if (!output->isHorizontal()) { + realSize.transpose(); + } + rootObject->setProperty("itemSource", QStringLiteral("OutputIdentifier.qml")); + rootObject->setProperty("modeName", Utils::sizeToString(realSize)); + rootObject->setProperty("outputName", Utils::outputName(output)); + showOsd(); +} + +void Osd::showActionSelector() +{ + if (!initOsd()) { + return; + } + + m_outputGeometry = m_output->geometry(); + auto *rootObject = m_osdObject->rootObject(); + rootObject->setProperty("itemSource", QStringLiteral("OsdSelector.qml")); + rootObject->setProperty("timeout", 0); + rootObject->setProperty("outputOnly", false); + auto osdItem = rootObject->property("osdItem").value(); + connect(osdItem, SIGNAL(clicked(int)), + this, SLOT(onOsdActionSelected(int))); + m_timeout = 0; // no timeout for this one + + showOsd(); +} + +void Osd::onOsdActionSelected(int action) +{ + Q_EMIT osdActionSelected(static_cast(action)); + hideOsd(); +} + +void Osd::onOutputAvailabilityChanged() +{ + if (!m_output || !m_output->isConnected() || !m_output->isEnabled() || !m_output->currentMode()) { + hideOsd(); + } +} + +void Osd::updatePosition() +{ + if (!initOsd()) { + return; + } + + const auto geometry = m_output->geometry(); + if (!geometry.isValid()) { + hideOsd(); + } + + auto *rootObject = m_osdObject->rootObject(); + + const int dialogWidth = rootObject->property("width").toInt(); + const int dialogHeight = rootObject->property("height").toInt(); + const int relx = geometry.x(); + const int rely = geometry.y(); + const int pos_x = relx + (geometry.width() - dialogWidth) / 2; + const int pos_y = rely + (geometry.height() - dialogHeight) / 2; + + rootObject->setProperty("x", pos_x); + rootObject->setProperty("y", pos_y); +} + +void Osd::showOsd() +{ + m_osdTimer->stop(); + + auto *rootObject = m_osdObject->rootObject(); + + // only animate on X11, wayland plugin doesn't support this and + // pukes loads of warnings into our logs + if (qGuiApp->platformName() == QLatin1String("xcb")) { + if (rootObject->property("timeout").toInt() > 0) { + rootObject->setProperty("animateOpacity", false); + rootObject->setProperty("opacity", 1); + rootObject->setProperty("visible", true); + rootObject->setProperty("animateOpacity", true); + rootObject->setProperty("opacity", 0); + } else { + rootObject->setProperty("visible", true); + } + } else { + rootObject->setProperty("visible", true); + } + QTimer::singleShot(0, this, &Osd::updatePosition); + if (m_timeout > 0) { + m_osdTimer->start(m_timeout); + } +} + +void Osd::hideOsd() +{ + if (!initOsd()) { + return; + } + + auto *rootObject = m_osdObject->rootObject(); + if (!rootObject) { + return; + } + rootObject->setProperty("visible", false); +} + diff --git a/kded/osd.h b/kded/osd.h new file mode 100644 index 0000000..55e452f --- /dev/null +++ b/kded/osd.h @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KSCREEN_OSD_H +#define KSCREEN_OSD_H + +#include +#include +#include + +#include + +#include "osdmanager.h" + +namespace KDeclarative { + class QmlObject; +} + +class QTimer; + +namespace KScreen { + +class Osd : public QObject { + + Q_OBJECT + +public: + Osd(const KScreen::OutputPtr output, QObject *parent = nullptr); + ~Osd() override; + + void showGenericOsd(const QString &icon, const QString &text); + void showOutputIdentifier(const KScreen::OutputPtr output); + void showActionSelector(); + void hideOsd(); + +Q_SIGNALS: + void osdActionSelected(OsdAction::Action action); + +private Q_SLOTS: + void onOsdActionSelected(int action); + void onOutputAvailabilityChanged(); + +private: + bool initOsd(); + void showOsd(); + void updatePosition(); + + KScreen::OutputPtr m_output; + QRect m_outputGeometry; + KDeclarative::QmlObject *m_osdObject = nullptr; + QTimer *m_osdTimer = nullptr; + int m_timeout = 0; + +}; + +} // ns + +#endif // KSCREEN_OSD_H diff --git a/kded/osdmanager.cpp b/kded/osdmanager.cpp new file mode 100644 index 0000000..4982c3f --- /dev/null +++ b/kded/osdmanager.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "osdmanager.h" +#include "osd.h" +#include "kscreen_daemon_debug.h" + +#include +#include +#include + +#include + +#include + +namespace KScreen { + +OsdManager* OsdManager::s_instance = nullptr; + +OsdAction::OsdAction(QObject *parent) + : QObject(parent) +{ +} + +class OsdActionImpl : public OsdAction +{ + Q_OBJECT +public: + OsdActionImpl(QObject *parent = nullptr) + : OsdAction(parent) + {} + + void setOsd(Osd *osd) { + connect(osd, &Osd::osdActionSelected, + this, [this](Action action) { + Q_EMIT selected(action); + deleteLater(); + }); + } +}; + +OsdManager::OsdManager(QObject *parent) + : QObject(parent) + , m_cleanupTimer(new QTimer(this)) +{ + qmlRegisterUncreatableType("org.kde.KScreen", 1, 0, "OsdAction", "You cannot create OsdAction"); + + // free up memory when the osd hasn't been used for more than 1 minute + m_cleanupTimer->setInterval(60000); + m_cleanupTimer->setSingleShot(true); + connect(m_cleanupTimer, &QTimer::timeout, this, [this]() { + hideOsd(); + }); + QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kscreen.osdService")); + if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/kscreen/osdService"), this, QDBusConnection::ExportAllSlots)) { + qCWarning(KSCREEN_KDED) << "Failed to registerObject"; + } +} + +void OsdManager::hideOsd() +{ + qDeleteAll(m_osds); + m_osds.clear(); +} + +OsdManager::~OsdManager() +{ +} + +OsdManager* OsdManager::self() +{ + if (!OsdManager::s_instance) { + s_instance = new OsdManager(); + } + return s_instance; +} + +void OsdManager::showOutputIdentifiers() +{ + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, &OsdManager::slotIdentifyOutputs); +} + +void OsdManager::slotIdentifyOutputs(KScreen::ConfigOperation *op) +{ + if (op->hasError()) { + return; + } + + const KScreen::ConfigPtr config = qobject_cast(op)->config(); + + Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + auto osd = m_osds.value(output->name()); + if (!osd) { + osd = new KScreen::Osd(output, this); + m_osds.insert(output->name(), osd); + } + osd->showOutputIdentifier(output); + } + m_cleanupTimer->start(); +} + +void OsdManager::showOsd(const QString& icon, const QString& text) +{ + qDeleteAll(m_osds); + m_osds.clear(); + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, [this, icon, text] (KScreen::ConfigOperation *op) { + if (op->hasError()) { + return; + } + + const KScreen::ConfigPtr config = qobject_cast(op)->config(); + + Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + auto osd = m_osds.value(output->name()); + if (!osd) { + osd = new KScreen::Osd(output, this); + m_osds.insert(output->name(), osd); + } + osd->showGenericOsd(icon, text); + } + m_cleanupTimer->start(); + } + ); +} + +OsdAction *OsdManager::showActionSelector() +{ + hideOsd(); + + OsdActionImpl *action = new OsdActionImpl(this); + connect(action, &OsdActionImpl::selected, + this, [this]() { + for (auto osd : qAsConst(m_osds)) { + osd->hideOsd(); + } + }); + connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, + this, [this, action](const KScreen::ConfigOperation *op) { + if (op->hasError()) { + qCWarning(KSCREEN_KDED) << op->errorString(); + return; + } + + // Show selector on alll enabled screens + const auto outputs = op->config()->outputs(); + KScreen::OutputPtr osdOutput; + for (const auto &output : outputs) { + if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { + continue; + } + + // Prefer laptop screen + if (output->type() == KScreen::Output::Panel) { + osdOutput = output; + break; + } + + // Fallback to primary + if (output->isPrimary()) { + osdOutput = output; + break; + } + } + // no laptop or primary screen, just take the first usable one + if (!osdOutput) { + for (const auto &output : outputs) { + if (output->isConnected() && output->isEnabled() && output->currentMode()) { + osdOutput = output; + break; + } + } + } + + if (!osdOutput) { + // huh!? + return; + } + + KScreen::Osd* osd = nullptr; + if (m_osds.contains(osdOutput->name())) { + osd = m_osds.value(osdOutput->name()); + } else { + osd = new KScreen::Osd(osdOutput, this); + m_osds.insert(osdOutput->name(), osd); + } + action->setOsd(osd); + osd->showActionSelector(); + m_cleanupTimer->start(); + } + ); + + return action; +} + + +} + +#include "osdmanager.moc" diff --git a/kded/osdmanager.h b/kded/osdmanager.h new file mode 100644 index 0000000..75c6250 --- /dev/null +++ b/kded/osdmanager.h @@ -0,0 +1,81 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef OSDM_H +#define OSDM_H + +#include +#include +#include +#include + +class QmlObject; + +namespace KScreen { + +class ConfigOperation; +class Osd; +class Output; + +class OsdAction : public QObject +{ + Q_OBJECT +public: + enum Action { + NoAction, + SwitchToExternal, + SwitchToInternal, + Clone, + ExtendLeft, + ExtendRight + }; + Q_ENUM(Action) + +Q_SIGNALS: + void selected(Action action); + +protected: + explicit OsdAction(QObject *parent = nullptr); +}; + + +class OsdManager : public QObject { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kscreen.osdService") + +public: + ~OsdManager() override; + static OsdManager* self(); + +public Q_SLOTS: + void showOutputIdentifiers(); + void showOsd(const QString &icon, const QString &text); + void hideOsd(); + OsdAction *showActionSelector(); + +private: + OsdManager(QObject *parent = nullptr); + void slotIdentifyOutputs(KScreen::ConfigOperation *op); + QMap m_osds; + + static OsdManager* s_instance; + QTimer* m_cleanupTimer; +}; + +} // ns +#endif // OSDM_H diff --git a/kded/qml/Osd.qml b/kded/qml/Osd.qml new file mode 100644 index 0000000..e04a1e0 --- /dev/null +++ b/kded/qml/Osd.qml @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.0 +import QtQuick.Window 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.Dialog { + id: root + location: PlasmaCore.Types.Floating + type: PlasmaCore.Dialog.OnScreenDisplay + outputOnly: true + + // OSD Timeout in msecs - how long it will stay on the screen + property int timeout: 5000 + + // Icon name to display + property string icon + property string infoText + property string outputName + property string modeName + property bool animateOpacity: false + property string itemSource + property QtObject osdItem + + Behavior on opacity { + SequentialAnimation { + // prevent press and hold from flickering + PauseAnimation { duration: root.timeout * 0.8 } + + NumberAnimation { + duration: root.timeout * 0.2 + easing.type: Easing.InQuad + } + } + enabled: root.timeout > 0 && root.animateOpacity + } + + mainItem: Loader { + source: itemSource + onItemChanged: { + if (item != undefined) { + item.rootItem = root; + root.osdItem = item + } + } + + } +} diff --git a/kded/qml/OsdItem.qml b/kded/qml/OsdItem.qml new file mode 100644 index 0000000..43750f5 --- /dev/null +++ b/kded/qml/OsdItem.qml @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Martin Klapetek + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.extras 2.0 as PlasmaExtras + +Item { + property QtObject rootItem + + height: Math.min(units.gridUnit * 15, Screen.desktopAvailableHeight / 5) + width: height + + PlasmaCore.IconItem { + id: icon + height: parent.height - label.height - ((units.smallSpacing/2) * 3) + width: parent.width + source: rootItem.icon + } + + PlasmaExtras.Heading { + id: label + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Math.floor(units.smallSpacing / 2) + } + + text: rootItem.infoText + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + minimumPointSize: theme.defaultFont.pointSize + fontSizeMode: Text.HorizontalFit + } + Component.onCompleted: print("osditem loaded..." + root.infoText); +} diff --git a/kded/qml/OsdSelector.qml b/kded/qml/OsdSelector.qml new file mode 100644 index 0000000..0db6efd --- /dev/null +++ b/kded/qml/OsdSelector.qml @@ -0,0 +1,116 @@ +/* + * Copyright 2017 Daniel Vrátil + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +import org.kde.KScreen 1.0 + +Item { + id: root + property QtObject rootItem + + signal clicked(int actionId) + + height: Math.min(units.gridUnit * 15, Screen.desktopAvailableHeight / 5) + width: buttonRow.width + + PlasmaComponents.ButtonRow { + id: buttonRow + + exclusive: false + + height: parent.height - label.height - ((units.smallSpacing/2) * 3) + width: (actionRepeater.count * height) + ((actionRepeater.count - 1) * buttonRow.spacing); + + Repeater { + id: actionRepeater + model: [ + { + iconSource: "osd-shutd-laptop", + label: i18n("Switch to external screen"), + action: OsdAction.SwitchToExternal + }, + { + iconSource: "osd-shutd-screen", + label: i18n("Switch to laptop screen"), + action: OsdAction.SwitchToInternal + }, + { + iconSource: "osd-duplicate", + label: i18n("Unify outputs"), + action: OsdAction.Clonse + }, + { + iconSource: "osd-sbs-left", + label: i18n("Extend to left"), + action: OsdAction.ExtendLeft + }, + { + iconSource: "osd-sbs-sright", + label: i18n("Extend to right"), + action: OsdAction.ExtendRight + }, + { + iconSource: "dialog-cancel", + label: i18n("Leave unchanged"), + action: OsdAction.NoAction + } + ] + delegate: PlasmaComponents.Button { + PlasmaCore.IconItem { + source: modelData.iconSource + height: buttonRow.height - ((units.smallSpacing / 2) * 3) + width: height + anchors.centerIn: parent + } + height: parent.height + width: height + + onHoveredChanged: rootItem.infoText = (hovered ? modelData.label : "") + + onClicked: root.clicked(modelData.action) + } + } + } + + // TODO: keep? remove? + PlasmaExtras.Heading { + id: label + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Math.floor(units.smallSpacing / 2) + } + + text: rootItem.infoText + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + minimumPointSize: theme.defaultFont.pointSize + fontSizeMode: Text.HorizontalFit + } + + Component.onCompleted: print("OsdSelector loaded..."); +} + diff --git a/kded/qml/OutputIdentifier.qml b/kded/qml/OutputIdentifier.qml new file mode 100644 index 0000000..6245928 --- /dev/null +++ b/kded/qml/OutputIdentifier.qml @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Sebastian Kügler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.5 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +ColumnLayout { + + property QtObject rootItem + + property string outputName: rootItem ? rootItem.outputName : "" + property string modeName: rootItem ? rootItem.modeName : "" + + PlasmaComponents.Label { + id: displayName + + Layout.maximumWidth: Screen.width * 0.8 + Layout.maximumHeight: Screen.height * 0.8 + Layout.margins: units.largeSpacing + Layout.bottomMargin: units.smallSpacing + + text: root.outputName + font.pointSize: theme.defaultFont.pointSize * 3 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + maximumLineCount: 2 + elide: Text.ElideLeft + } + + PlasmaComponents.Label { + id: modeLabel; + + Layout.fillWidth: true + Layout.bottomMargin: units.largeSpacing + + text: root.modeName; + horizontalAlignment: Text.AlignHCenter; + } +} diff --git a/kded/serializer.cpp b/kded/serializer.cpp index f2aee5c..bcb202d 100644 --- a/kded/serializer.cpp +++ b/kded/serializer.cpp @@ -1,325 +1,328 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "serializer.h" -#include "debug.h" +#include "kscreen_daemon_debug.h" #include "generator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include QString Serializer::sConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); void Serializer::setConfigPath(const QString &path) { sConfigPath = path; if (!sConfigPath.endsWith(QLatin1Char('/'))) { sConfigPath += QLatin1Char('/'); } } QString Serializer::configFileName(const QString &configId) { if (!QDir().mkpath(sConfigPath)) { return QString(); } return sConfigPath % configId; } QString Serializer::configId(const KScreen::ConfigPtr ¤tConfig) { if (!currentConfig) { return QString(); } KScreen::OutputList outputs = currentConfig->outputs(); QStringList hashList; //qCDebug(KSCREEN_KDED) << "Calculating config ID for" << currentConfig.data(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } //qCDebug(KSCREEN_KDED) << "\tPart of the Id: " << Serializer::outputId(output); hashList.insert(0, Serializer::outputId(output)); } qSort(hashList.begin(), hashList.end()); const QByteArray hash = QCryptographicHash::hash(hashList.join(QString()).toLatin1(), QCryptographicHash::Md5).toHex(); //qCDebug(KSCREEN_KDED) << "\tConfig ID:" << hash; return hash; } bool Serializer::configExists(const KScreen::ConfigPtr &config) { return Serializer::configExists(Serializer::configId(config)); } bool Serializer::configExists(const QString &id) { return QFile::exists(sConfigPath % id); } KScreen::ConfigPtr Serializer::config(const KScreen::ConfigPtr ¤tConfig, const QString &id) { KScreen::ConfigPtr config = currentConfig->clone(); QFile file(configFileName(id)); if (!file.open(QIODevice::ReadOnly)) { qCDebug(KSCREEN_KDED) << "failed to open file" << id; return KScreen::ConfigPtr(); } KScreen::OutputList outputList = config->outputs(); QJsonDocument parser; QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); Q_FOREACH(KScreen::OutputPtr output, outputList) { if (!output->isConnected() && output->isEnabled()) { output->setEnabled(false); } } QSize screenSize; Q_FOREACH(const QVariant &info, outputs) { KScreen::OutputPtr output = Serializer::findOutput(config, info.toMap()); if (!output) { continue; } if (output->isEnabled()) { const QRect geom = output->geometry(); if (geom.x() + geom.width() > screenSize.width()) { screenSize.setWidth(geom.x() + geom.width()); } if (geom.y() + geom.height() > screenSize.height()) { screenSize.setHeight(geom.y() + geom.height()); } } outputList.remove(output->id()); outputList.insert(output->id(), output); } config->setOutputs(outputList); config->screen()->setCurrentSize(screenSize); return config; } bool Serializer::saveConfig(const KScreen::ConfigPtr &config, const QString &configId) { if (!config || configId.isEmpty()) { return false; } const KScreen::OutputList outputs = config->outputs(); QVariantList outputList; Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } QVariantMap info; info[QStringLiteral("id")] = Serializer::outputId(output); info[QStringLiteral("primary")] = output->isPrimary(); info[QStringLiteral("enabled")] = output->isEnabled(); info[QStringLiteral("rotation")] = output->rotation(); + info[QStringLiteral("scale")] = output->scale(); QVariantMap pos; pos[QStringLiteral("x")] = output->pos().x(); pos[QStringLiteral("y")] = output->pos().y(); info[QStringLiteral("pos")] = pos; if (output->isEnabled()) { const KScreen::ModePtr mode = output->currentMode(); if (!mode) { qWarning() << "CurrentMode is null" << output->name(); return false; } QVariantMap modeInfo; modeInfo[QStringLiteral("refresh")] = mode->refreshRate(); QVariantMap modeSize; modeSize[QStringLiteral("width")] = mode->size().width(); modeSize[QStringLiteral("height")] = mode->size().height(); modeInfo[QStringLiteral("size")] = modeSize; info[QStringLiteral("mode")] = modeInfo; } info[QStringLiteral("metadata")] = Serializer::metadata(output); outputList.append(info); } QFile file(configFileName(configId)); if (!file.open(QIODevice::WriteOnly)) { qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); return false; } file.write(QJsonDocument::fromVariant(outputList).toJson()); qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); return true; } void Serializer::removeConfig(const QString &id) { QFile::remove(configFileName(id)); } bool Serializer::moveConfig(const QString &srcId, const QString &destId) { const QFile srcFile(configFileName(srcId)); if (srcFile.exists()) { removeConfig(destId); if (QFile::copy(configFileName(srcId), configFileName(destId))) { removeConfig(srcId); qCDebug(KSCREEN_KDED) << "Restored config" << srcId << "to" << destId; return true; } } return false; } KScreen::OutputPtr Serializer::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info) { - KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, + const KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, // to be able to tell apart multiple identical outputs, these need special treatment QStringList duplicateIds; QStringList allIds; - Q_FOREACH (KScreen::OutputPtr output, outputs) { + allIds.reserve(outputs.count()); + Q_FOREACH (const KScreen::OutputPtr &output, outputs) { const auto outputId = Serializer::outputId(output); if (allIds.contains(outputId) && !duplicateIds.contains(outputId)) { duplicateIds << outputId; } allIds << outputId; } allIds.clear(); Q_FOREACH(KScreen::OutputPtr output, outputs) { if (!output->isConnected()) { continue; } const auto outputId = Serializer::outputId(output); if (outputId != info[QStringLiteral("id")].toString()) { continue; } // We may have identical outputs connected, these will have the same id in the config // in order to find the right one, also check the output's name (usually the connector) if (!output->name().isEmpty() && duplicateIds.contains(outputId)) { const auto metadata = info[QStringLiteral("metadata")].toMap(); const auto outputName = metadata[QStringLiteral("name")].toString(); if (output->name() != outputName) { continue; } } const QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); output->setPos(point); output->setPrimary(info[QStringLiteral("primary")].toBool()); output->setEnabled(info[QStringLiteral("enabled")].toBool()); output->setRotation(static_cast(info[QStringLiteral("rotation")].toInt())); + output->setScale(info.value(QStringLiteral("scale"), 1).toInt()); const QVariantMap modeInfo = info[QStringLiteral("mode")].toMap(); const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); const QSize size = QSize(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt()); qCDebug(KSCREEN_KDED) << "Finding a mode for" << size << "@" << modeInfo[QStringLiteral("refresh")].toFloat(); KScreen::ModeList modes = output->modes(); KScreen::ModePtr matchingMode; Q_FOREACH(const KScreen::ModePtr &mode, modes) { if (mode->size() != size) { continue; } if (!qFuzzyCompare(mode->refreshRate(), modeInfo[QStringLiteral("refresh")].toFloat())) { continue; } qCDebug(KSCREEN_KDED) << "\tFound: " << mode->id() << " " << mode->size() << "@" << mode->refreshRate(); matchingMode = mode; break; } if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to find a matching mode - this means that our config is corrupted" "or a different device with the same serial number has been connected (very unlikely)." "Falling back to preferred modes."; matchingMode = output->preferredMode(); if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to get a preferred mode, falling back to biggest mode."; matchingMode = Generator::biggestMode(modes); if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to get biggest mode. Which means there are no modes. Turning off the screen."; output->setEnabled(false); return output; } } } output->setCurrentModeId(matchingMode->id()); return output; } qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted" "or a different device with the same serial number has been connected (very unlikely)."; return KScreen::OutputPtr(); } QString Serializer::outputId(const KScreen::OutputPtr &output) { if (output->edid() && output->edid()->isValid()) { return output->edid()->hash(); } return output->name(); } QVariantMap Serializer::metadata(const KScreen::OutputPtr &output) { QVariantMap metadata; metadata[QStringLiteral("name")] = output->name(); if (!output->edid() || !output->edid()->isValid()) { return metadata; } metadata[QStringLiteral("fullname")] = output->edid()->deviceId(); return metadata; } diff --git a/kscreen.categories b/kscreen.categories new file mode 100644 index 0000000..c0a9ab1 --- /dev/null +++ b/kscreen.categories @@ -0,0 +1,2 @@ +kscreen.kded kscreen kded (kscreen) +kscreen.kcm kscreen kcm (kscreen) diff --git a/plasma/plasma-applet-kscreen.desktop.cmake b/plasma/plasma-applet-kscreen.desktop.cmake index 94b96ae..a00ff00 100644 --- a/plasma/plasma-applet-kscreen.desktop.cmake +++ b/plasma/plasma-applet-kscreen.desktop.cmake @@ -1,104 +1,108 @@ [Desktop Entry] Name=Quick Display Configuration Name[ar]=ضبط سريع للعرض -Name[ast]=Configuración rápida de pantalla Name[bs]=Brza konfiguracija ekrana Name[ca]=Configuració ràpida de la pantalla Name[ca@valencia]=Configuració ràpida de la pantalla Name[cs]=Rychlé nastavení zobrazení Name[da]=Hurtig konfiguration af skærme Name[de]=Schnelle Anzeige-Einrichtung Name[el]=Γρήγορη διαμόρφωση οθόνης Name[en_GB]=Quick Display Configuration Name[es]=Configuración rápida de la pantalla Name[et]=Ekraani kiirseadistamine +Name[eu]=Bistaratzaileen konfigurazio azkarra Name[fi]=Näyttöasetusten pikasäätö Name[fr]=Configuration rapide de l'affichage Name[gl]=Configuración rápida da pantalla Name[he]=הגדרות מסך מהירות Name[hu]=Gyors megjelenítő-beállítás +Name[ia]=Configuration rapide de monstrator +Name[id]=Konfigurasi Display Cepat Name[it]=Configurazione rapida dello schermo Name[ko]=빠른 화면 설정 Name[lt]=Greita ekrano konfigūracija Name[nb]=Hurtig skjermoppsett Name[nl]=Snel instellen van scherm Name[nn]=Kjapt skjermoppsett Name[pa]=ਤੁਰੰਤ ਡਿਸਪਲੇਅ ਸੰਰਚਨਾ Name[pl]=Szybkie ustawienia ekranu Name[pt]=Configuração Rápida do Ecrã Name[pt_BR]=Configuração rápida da tela Name[ro]=Configurare rapidă afișaj Name[ru]=Быстрая настройка экрана Name[sk]=Rýchle nastavenie obrazovky Name[sl]=Hitre nastavitve zaslona Name[sr]=Брзо подешавање екрана Name[sr@ijekavian]=Брзо подешавање екрана Name[sr@ijekavianlatin]=Brzo podešavanje ekrana Name[sr@latin]=Brzo podešavanje ekrana Name[sv]=Snabbinställning av skärm Name[tr]=Hızlı Ekran Yapılandırması Name[uk]=Швидке налаштовування дисплея Name[x-test]=xxQuick Display Configurationxx Name[zh_CN]=快捷显示管理 Name[zh_TW]=快速顯示設定 Comment=Quick configuration of a new display Comment[ar]=ضبط سريع لعرض جديد -Comment[ast]=Configuración rápida d'una pantalla nueva Comment[bs]=Brza konfiguracija novog ekrana Comment[ca]=Configuració ràpida d'una nova pantalla Comment[ca@valencia]=Configuració ràpida d'una nova pantalla Comment[cs]=Rychlé nastavení nového zobrazení Comment[da]=Hurtig konfiguration af en ny skærm Comment[de]=Schnelle Einrichtung einer neuen Anzeige Comment[el]=Γρήγορη διαμόρφωση μιας νέας οθόνης Comment[en_GB]=Quick configuration of a new display Comment[es]=Configuración rápida de una nueva pantalla Comment[et]=Uue ekraani kiirseadistamine +Comment[eu]=Bistaratzaile berri baten konfigurazio azkarra Comment[fi]=Näyttöasetusten pikasäätö Comment[fr]=Configuration rapide d'un nouvel affichage Comment[gl]=Configura rapidamente unha nova pantalla Comment[he]=הגדרה מהירה של תצוגה חדשה Comment[hu]=Új megjelenítők gyors beállítása +Comment[ia]=Configuration rapide de un nove monstrator +Comment[id]=Konfigurasi cepat sebuah display baru Comment[it]=Configurazione rapida di un nuovo schermo Comment[ko]=새 디스플레이 빠른 설정 Comment[lt]=Grupės naujo ekrano konfigūracija Comment[nb]=Hurtig oppsett av en ny skjerm Comment[nl]=Snel instellen van een nieuwe scherm Comment[nn]=Kjapt oppsett av ny skjerm Comment[pa]=ਨਵੇਂ ਡਿਸਪਲੇਅ ਲਈ ਤੁਰੰਤ ਸੰਰਚਨਾ Comment[pl]=Szybkie ustawienia nowego ekranu Comment[pt]=Configuração rápida de um novo ecrã Comment[pt_BR]=Configuração rápida de uma nova tela Comment[ro]=Configurare rapidă a unui nou afișaj Comment[ru]=Быстрая настройка нового экрана Comment[sk]=Rýchle nastavenie novej obrazovky Comment[sl]=Hitre nastavitve novega zaslona Comment[sr]=Брзо подешавање за нов екран Comment[sr@ijekavian]=Брзо подешавање за нов екран Comment[sr@ijekavianlatin]=Brzo podešavanje za nov ekran Comment[sr@latin]=Brzo podešavanje za nov ekran Comment[sv]=Snabbinställning av en ny skärm Comment[tr]=Yeni bir ekranın hızlı yapılandırması Comment[uk]=Швидке налаштовування нового дисплея Comment[x-test]=xxQuick configuration of a new displayxx Comment[zh_CN]=快捷配置新显示器 Comment[zh_TW]=快速設定新的顯示 Icon=preferences-desktop-display-randr Type=Service X-KDE-ServiceTypes=Plasma/Applet,Plasma/PopupApplet X-Plasma-DefaultSize=290,150 X-Plasma-NotificationArea=true X-Plasma-ConfigPlugins=kscreen X-KDE-Library=plasma_applet_kscreen X-KDE-PluginInfo-Author=Dan Vrátil X-KDE-PluginInfo-Email=dvratil@redhat.com X-KDE-PluginInfo-Name=org.kde.plasma.kscreen X-KDE-PluginInfo-Version=@KSCREEN_VERSION@ X-KDE-PluginInfo-Website=http://www.kde.org X-KDE-PluginInfo-Category=System Information X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bf433a0..5f0861b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(configmodule) add_subdirectory(kded) +add_subdirectory(osd) diff --git a/tests/kded/CMakeLists.txt b/tests/kded/CMakeLists.txt index 6fb3619..83692eb 100644 --- a/tests/kded/CMakeLists.txt +++ b/tests/kded/CMakeLists.txt @@ -1,30 +1,30 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR} ) macro(ADD_KDED_TEST testname) set(test_SRCS ${testname}.cpp ${CMAKE_SOURCE_DIR}/kded/generator.cpp ${CMAKE_SOURCE_DIR}/kded/device.cpp - ${CMAKE_SOURCE_DIR}/kded/debug.cpp ${CMAKE_SOURCE_DIR}/kded/serializer.cpp #${CMAKE_SOURCE_DIR}/kded/daemon.cpp ) + ecm_qt_declare_logging_category(test_SRCS HEADER kscreen_daemon_debug.h IDENTIFIER KSCREEN_KDED CATEGORY_NAME kscreen.kded) qt5_add_dbus_interface(test_SRCS ${CMAKE_SOURCE_DIR}/kded/org.freedesktop.DBus.Properties.xml freedesktop_interface ) add_executable(${testname} ${test_SRCS}) add_dependencies(${testname} kscreen) # make sure the dbus interfaces are generated target_compile_definitions(${testname} PRIVATE "-DTEST_DATA=\"${CMAKE_CURRENT_SOURCE_DIR}/\"") target_link_libraries(${testname} Qt5::Test Qt5::DBus Qt5::Gui KF5::Screen) add_test(kscreen-kded-${testname} ${testname}) ecm_mark_as_test(${testname}) endmacro() add_kded_test(testgenerator) add_kded_test(serializertest) #add_kded_test(testdaemon) diff --git a/tests/osd/CMakeLists.txt b/tests/osd/CMakeLists.txt new file mode 100644 index 0000000..a98ed36 --- /dev/null +++ b/tests/osd/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/kcm/src + ${CMAKE_BINARY_DIR}/kded +) + +add_executable(osdtest main.cpp + osdtest.cpp + ../../kded/osd.cpp + ../../kded/osdmanager.cpp + ../../kcm/src/utils.cpp +) + +target_link_libraries(osdtest Qt5::Core + Qt5::DBus + Qt5::Quick + Qt5::Qml + KF5::Screen + KF5::I18n + KF5::Declarative +) + +add_test(kscreen-kded-osdtest osdtest) +ecm_mark_as_test(osdtest) diff --git a/tests/osd/main.cpp b/tests/osd/main.cpp new file mode 100644 index 0000000..4bbff93 --- /dev/null +++ b/tests/osd/main.cpp @@ -0,0 +1,64 @@ +/************************************************************************************* + * Copyright 2014-2016 by Sebastian Kügler * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + *************************************************************************************/ + +#include "osdtest.h" + +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QCommandLineOption dbus = QCommandLineOption(QStringList() << QStringLiteral("d") << "dbus", + QStringLiteral("Call over dbus")); + QCommandLineOption outputid = QCommandLineOption(QStringList() << QStringLiteral("o") << "outputidentifiers", + QStringLiteral("Show output identifier")); + QCommandLineOption icon = QCommandLineOption(QStringList() << QStringLiteral("i") << "icon", + QStringLiteral("Icon to use for OSD"), QStringLiteral("preferences-desktop-display-randr")); + QCommandLineOption message = QCommandLineOption(QStringList() << QStringLiteral("m") << "message", + QStringLiteral("Icon to use for OSD"), QStringLiteral("OSD Test")); + QCommandLineOption selector = QCommandLineOption({ QStringLiteral("s"), QStringLiteral("selector") }, + QStringLiteral("Show new screen action selector")); + KScreen::OsdTest osdtest; + QCommandLineParser parser; + parser.addHelpOption(); + parser.addOption(dbus); + parser.addOption(outputid); + parser.addOption(icon); + parser.addOption(message); + parser.addOption(selector); + parser.process(app); + + + + if (parser.isSet(dbus)) { + osdtest.setUseDBus(true); + } + if (parser.isSet(outputid)) { + osdtest.showOutputIdentifiers(); + } else if (parser.isSet(selector)) { + osdtest.showActionSelector(); + } else { + osdtest.showGenericOsd(parser.value(icon), parser.value(message)); + } + if (parser.isSet(outputid)) { + } + + return app.exec(); +} diff --git a/tests/osd/osdtest.cpp b/tests/osd/osdtest.cpp new file mode 100644 index 0000000..aedae2b --- /dev/null +++ b/tests/osd/osdtest.cpp @@ -0,0 +1,93 @@ +/************************************************************************************* + * Copyright 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 "osdtest.h" +#include "../../kded/osdmanager.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(KSCREEN_KDED, "kscreen.kded") + +namespace KScreen { +OsdTest::OsdTest(QObject *parent) + : QObject(parent) +{ +} + +OsdTest::~OsdTest() +{ +} + +void OsdTest::showOutputIdentifiers() +{ + if (!m_useDBus) { + QTimer::singleShot(5500, qApp, &QCoreApplication::quit); + KScreen::OsdManager::self()->showOutputIdentifiers(); + } else { + QDBusMessage msg = QDBusMessage::createMethodCall( + QLatin1Literal("org.kde.kscreen.osdService"), + QLatin1Literal("/org/kde/kscreen/osdService"), + QLatin1Literal("org.kde.kscreen.osdService"), + QLatin1Literal("showOutputIdentifiers") + ); + //msg << icon << text; + QDBusConnection::sessionBus().asyncCall(msg); + + qCWarning(KSCREEN_KDED) << "Sent dbus message."; + QTimer::singleShot(500, qApp, &QCoreApplication::quit); + } +} + + +void OsdTest::setUseDBus(bool yesno) +{ + m_useDBus = yesno; +} + +void OsdTest::showGenericOsd(const QString& icon, const QString& message) +{ + if (!m_useDBus) { + QTimer::singleShot(5500, qApp, &QCoreApplication::quit); + KScreen::OsdManager::self()->showOsd(!icon.isEmpty() ? icon : QStringLiteral("preferences-desktop-display-randr"), + !message.isEmpty() ? message : QStringLiteral("On-Screen-Display")); + } else { + qCWarning(KSCREEN_KDED) << "Implement me."; + QTimer::singleShot(100, qApp, &QCoreApplication::quit); + } +} + +void OsdTest::showActionSelector() +{ + if (!m_useDBus) { + auto action = KScreen::OsdManager::self()->showActionSelector(); + connect(action, &KScreen::OsdAction::selected, + [](KScreen::OsdAction::Action action) { + qCDebug(KSCREEN_KDED) << "Selected action:" << action; + qApp->quit(); + }); + } else { + qCWarning(KSCREEN_KDED) << "Implement me."; + QTimer::singleShot(100, qApp, &QCoreApplication::quit); + } +} + +} // ns diff --git a/tests/osd/osdtest.h b/tests/osd/osdtest.h new file mode 100644 index 0000000..3cf3c0a --- /dev/null +++ b/tests/osd/osdtest.h @@ -0,0 +1,48 @@ +/************************************************************************************* + * Copyright 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 * + *************************************************************************************/ + +#ifndef KSCREEN_OSDTEST_H +#define KSCREEN_OSDTEST_H + +#include + + +namespace KScreen +{ + +class OsdTest : public QObject +{ + Q_OBJECT + +public: + explicit OsdTest(QObject *parent = nullptr); + virtual ~OsdTest(); + + void setUseDBus(bool yesno); + + void showGenericOsd(const QString &icon, const QString &message); + void showOutputIdentifiers(); + void showActionSelector(); + +private: + bool m_useDBus = false; +}; + +} // namespace + +#endif // KSCREEN_OSDTEST_H