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 66c3c4a..a7c66a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,34 +1,36 @@ project(KScreen) -set(PROJECT_VERSION "5.10.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 Plasma) set(MIN_LIBKSCREEN_VERSION "5.2.91") find_package(KF5Screen ${MIN_LIBKSCREEN_VERSION} REQUIRED) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(ECMMarkAsTest) include(FeatureSummary) - +include(ECMQtDeclareLoggingCategory) add_subdirectory(icons) add_subdirectory(kcm) add_subdirectory(plasmoid) 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/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 5ba3b8d..ad26004 100644 --- a/kcm/kcm_kscreen.desktop.cmake +++ b/kcm/kcm_kscreen.desktop.cmake @@ -1,129 +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[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[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/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 931e136..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: 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 61e8df1..fbda695 100644 --- a/kcm/src/declarative/qmloutput.cpp +++ b/kcm/src/declarative/qmloutput.cpp @@ -1,587 +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() / 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() / 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 88dbd06..4a0498b 100644 --- a/kcm/src/kcm_kscreen.cpp +++ b/kcm/src/kcm_kscreen.cpp @@ -1,180 +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); - layout->setMargin(0); + 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 d54cbfa..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: 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 7f27c57..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-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-left")), i18n("90° Counterclockwise"), KScreen::Output::Left); - connect(mRotation, static_cast(&QComboBox::currentIndexChanged), + 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 0b3483b..3a7341e 100644 --- a/kcm/src/previewwidget.cpp +++ b/kcm/src/previewwidget.cpp @@ -1,87 +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; 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() { 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 c9ae9b6..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(); } int ScalingConfig::scaleDPI() const { return qRound(scaleFactor() * 96.0); } qreal ScalingConfig::scaleFactor() const { return ui.scaleSlider->value() / SLIDER_RATIO; } diff --git a/kcm/src/unifiedoutputconfig.cpp b/kcm/src/unifiedoutputconfig.cpp index 578e7a6..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-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-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/widget.cpp b/kcm/src/widget.cpp index ceb58a7..b5a8ab1 100644 --- a/kcm/src/widget.cpp +++ b/kcm/src/widget.cpp @@ -1,481 +1,484 @@ /* * 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 #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->setMinimumHeight(280); container->setMinimumHeight(280); splitter->addWidget(container); 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:"))); 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)); + 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 53caa9a..d95df89 100644 --- a/kcm/src/widget.h +++ b/kcm/src/widget.h @@ -1,102 +1,103 @@ /* * 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 QMLOutput; class QMLScreen; class ControlPanel; class PrimaryOutputCombo; class QPushButton; class QComboBox; class QQuickView; 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: 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; + QQuickView *mDeclarativeView = nullptr; + ControlPanel *mControlPanel = nullptr; - ProfilesModel *mProfilesModel; - PrimaryOutputCombo *mPrimaryCombo; - QComboBox *mProfilesCombo; + ProfilesModel *mProfilesModel = nullptr; + PrimaryOutputCombo *mPrimaryCombo = 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..317f743 100644 --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,32 +1,33 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kscreen\") include_directories(${CMAKE_CURRENT_BINARY_DIR}/../) set(kscreen_daemon_SRCS - debug.cpp daemon.cpp serializer.cpp generator.cpp device.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) 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) diff --git a/kded/daemon.cpp b/kded/daemon.cpp index 0d399ca..84d1544 100644 --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -1,434 +1,440 @@ /************************************************************************************* * 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 #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_monitoredConfig(nullptr) , m_iteration(Generator::None) , 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); 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::applyIdealConfig() { qCDebug(KSCREEN_KDED) << "Applying ideal config"; doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); } -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() { 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(); } void KScreenDaemon::resetDisplaySwitch() { qCDebug(KSCREEN_KDED) << "resetDisplaySwitch()"; m_iteration = Generator::None; } void KScreenDaemon::applyGenericConfig() { if (m_iteration == Generator::ExtendToRight) { m_iteration = Generator::None; } m_iteration = Generator::DisplaySwitchAction(static_cast(m_iteration) + 1); qCDebug(KSCREEN_KDED) << "displayButton: " << m_iteration; doApplyConfig(Generator::self()->displaySwitch(m_iteration)); } 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/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 88433df..769f8d2 100644 --- a/kded/generator.cpp +++ b/kded/generator.cpp @@ -1,648 +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->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->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 = 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()); 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()); 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 = 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()); 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 79e7061..dc89dd2 100644 --- a/kded/kscreen.desktop.cmake +++ b/kded/kscreen.desktop.cmake @@ -1,98 +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[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/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 new file mode 100644 index 0000000..a00ff00 --- /dev/null +++ b/plasma/plasma-applet-kscreen.desktop.cmake @@ -0,0 +1,108 @@ +[Desktop Entry] +Name=Quick Display Configuration +Name[ar]=ضبط سريع للعرض +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[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/plasmoid/plugin/kscreendoctor.cpp b/plasmoid/plugin/kscreendoctor.cpp index bdc9363..16ad0a8 100644 --- a/plasmoid/plugin/kscreendoctor.cpp +++ b/plasmoid/plugin/kscreendoctor.cpp @@ -1,134 +1,147 @@ /* Copyright 2017 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) 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 6 of version 3 of the license. 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, see . */ #include "kscreendoctor.h" #include #include #include #include #include #include KScreenDoctor::KScreenDoctor(QObject *parent) : QObject(parent) { connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, [=] (KScreen::ConfigOperation* op) { if (op->hasError()) { return; } m_monitoredConfig = qobject_cast(op)->config(); qDebug() << "Config" << m_monitoredConfig.data() << "is ready"; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); updateOutputs(); } ); } void KScreenDoctor::updateOutputs() { m_outputNames.clear(); m_outputNames << QStringLiteral("Fake"); Q_ASSERT(m_monitoredConfig); for (auto output: m_monitoredConfig->outputs()) { if (output->isConnected()) { m_outputNames << output->name(); } } qDebug() << "OUTPUTS:" << m_outputNames; emit outputNamesChanged(); // Current output still valid? if (m_currentOutput.isEmpty() || !m_outputNames.contains(m_currentOutput)) { m_currentOutput = m_outputNames.at(0); // Perhaps the primary output isn't the first, in that case, select the primary instead for (auto output: m_monitoredConfig->outputs()) { if (output->isConnected() && output->isPrimary()) { m_currentOutput = output->name(); } } emit currentOutputChanged(); } qDebug() << "CURRENT:" << m_currentOutput; } QStringList KScreenDoctor::outputNames() const { return m_outputNames; } QString KScreenDoctor::currentOutput() const { return m_currentOutput; } void KScreenDoctor::setCurrentOutput(const QString ¤tOutput) { if (m_currentOutput != currentOutput) { m_currentOutput = currentOutput; qDebug() << "Current output is now: " << m_currentOutput; emit currentOutputChanged(); } } +bool KScreenDoctor::autoRotate() const +{ + return m_autoRotate; +} + +void KScreenDoctor::setAutoRotate(bool rotate) +{ + if (rotate != m_autoRotate) { + m_autoRotate = rotate; + emit autoRotateChanged(); + } +} + int KScreenDoctor::currentOutputRotation() const { return 0; } void KScreenDoctor::setRotation(int rotation) { qDebug() << "Setting output" << m_currentOutput << "to" << rotation; connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, [=] (KScreen::ConfigOperation* op) { if (op->hasError()) { return; } auto config = qobject_cast(op)->config(); KScreen::OutputPtr currentOutput = nullptr; for (auto output: config->outputs()) { if (output->name() == m_currentOutput) { qDebug() << "Setting output rotation on " << output->name(); if (rotation == 0) { output->setRotation(KScreen::Output::None); } else if (rotation == 90) { output->setRotation(KScreen::Output::Right); } else if (rotation == 180) { output->setRotation(KScreen::Output::Inverted); } else if (rotation == 270) { output->setRotation(KScreen::Output::Left); } auto *op = new KScreen::SetConfigOperation(config); op->exec(); } } } ); } diff --git a/plasmoid/plugin/kscreendoctor.h b/plasmoid/plugin/kscreendoctor.h index 75778a5..6256af9 100644 --- a/plasmoid/plugin/kscreendoctor.h +++ b/plasmoid/plugin/kscreendoctor.h @@ -1,62 +1,67 @@ /* - Copyright 2017 Sebastian Kügler + Copyright 2017-2018 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) 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 6 of version 3 of the license. 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, see . */ #ifndef KSCREENDOCIMPORT_H #define KSCREENDOCIMPORT_H #include #include #include class KScreenDoctor : public QObject { Q_OBJECT Q_PROPERTY(QStringList outputNames READ outputNames NOTIFY outputNamesChanged) Q_PROPERTY(QString currentOutput READ currentOutput WRITE setCurrentOutput NOTIFY currentOutputChanged) Q_PROPERTY(int currentOutputRotation READ currentOutputRotation NOTIFY currentOutputRotationChanged) + Q_PROPERTY(bool autoRotate READ autoRotate WRITE setAutoRotate NOTIFY autoRotateChanged) public: explicit KScreenDoctor(QObject *parent = Q_NULLPTR); + bool autoRotate() const; QStringList outputNames() const; QString currentOutput() const; int currentOutputRotation() const; public Q_SLOTS: + void setAutoRotate(bool rotate); void setCurrentOutput(const QString &outputName); Q_INVOKABLE void setRotation(int rotation); Q_SIGNALS: + void autoRotateChanged() const; void outputNamesChanged() const; void currentOutputChanged() const; void currentOutputRotationChanged() const; private: void updateOutputs(); + bool m_autoRotate; QStringList m_outputNames; QString m_currentOutput; KScreen::ConfigPtr m_monitoredConfig = nullptr; }; #endif // KSCREENDOCIMPORT_H 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)