diff --git a/README.md b/README.md index 56064a3..4f12b29 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ sudo pacman -S gcc cmake qt5-base qt5-quickcontrols2 networkmanager-qt modemmana Note that when using apt, the names of certain packages are different: ```shell -sudo apt install qtbase5-dev qtquickcontrols2-5-dev modemmanager-qt-dev libqt5sensors5-dev libkf5networkmanagerqt-dev libkf5screen-dev libkf5bluezqt-dev libkf5kio-dev cmake qtdeclarative5-dev libcanberra-dev libpulse-dev libcanberra-pulse extra-cmake-modules qttools5-dev qttools5-dev-tools +sudo apt install qtbase5-dev qtquickcontrols2-5-dev modemmanager-qt-dev libkf5networkmanagerqt-dev libkf5bluezqt-dev libkf5kio-dev cmake qtdeclarative5-dev libcanberra-dev libpulse-dev libcanberra-pulse extra-cmake-modules qttools5-dev qttools5-dev-tools ``` ## Build and Install diff --git a/debian/control b/debian/control index 3f303d3..cd5101d 100644 --- a/debian/control +++ b/debian/control @@ -7,10 +7,8 @@ Build-Depends: cmake, extra-cmake-modules, libkf5networkmanagerqt-dev, libkf5kio-dev, - libkf5screen-dev, libkf5bluezqt-dev, modemmanager-qt-dev, - libqt5sensors5-dev, libcanberra-dev, libpulse-dev, sound-theme-freedesktop, @@ -25,6 +23,5 @@ Homepage: https://cutefishos.com Package: libcutefish Architecture: any Depends: ${misc:Depends}, - ${shlibs:Depends}, - libkf5screen-bin + ${shlibs:Depends} Description: CutefishOS Library diff --git a/screen/CMakeLists.txt b/screen/CMakeLists.txt index fee3c21..341dcb9 100644 --- a/screen/CMakeLists.txt +++ b/screen/CMakeLists.txt @@ -1,17 +1,18 @@ +cmake_minimum_required(VERSION 3.5) +project(cutefish-screen LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + set(SCREEN_SRCS - common/control.cpp - common/globals.cpp - common/orientation_sensor.cpp - common/utils.cpp - confighandler.cpp - outputmodel.cpp - plugin.cpp - screen.cpp + qt5/output.cpp + qt5/outputmodel.cpp + qt5/plugin.cpp + qt5/screen.cpp ) -find_package(KF5Screen REQUIRED) -find_package(KF5KIO REQUIRED) -find_package(Qt5 REQUIRED COMPONENTS Test Sensors) +find_package(Qt5 REQUIRED COMPONENTS Core Gui Quick) add_library(cutefishscreen_qmlplugins SHARED ${SCREEN_SRCS}) @@ -19,13 +20,7 @@ target_link_libraries (cutefishscreen_qmlplugins Qt5::Core Qt5::Quick Qt5::Gui - Qt5::DBus - Qt5::Sensors - Qt5::Test - - KF5::Screen - KF5::KIOCore ) install(TARGETS cutefishscreen_qmlplugins DESTINATION ${INSTALL_QMLDIR}/Cutefish/Screen) -install(FILES qmldir DESTINATION ${INSTALL_QMLDIR}/Cutefish/Screen) \ No newline at end of file +install(FILES qmldir DESTINATION ${INSTALL_QMLDIR}/Cutefish/Screen) diff --git a/screen/qt5/output.cpp b/screen/qt5/output.cpp new file mode 100644 index 0000000..c5cf9c0 --- /dev/null +++ b/screen/qt5/output.cpp @@ -0,0 +1,2 @@ +#include "output.h" + diff --git a/screen/qt5/output.h b/screen/qt5/output.h new file mode 100644 index 0000000..fd8ca7c --- /dev/null +++ b/screen/qt5/output.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +class Output +{ + Q_GADGET + +public: + enum Rotation { + None = 1, + Left = 2, + Inverted = 4, + Right = 8, + }; + Q_ENUM(Rotation) +}; + diff --git a/screen/qt5/outputmodel.cpp b/screen/qt5/outputmodel.cpp new file mode 100644 index 0000000..4c89122 --- /dev/null +++ b/screen/qt5/outputmodel.cpp @@ -0,0 +1,150 @@ +#include "outputmodel.h" + +#include +#include + +Qt5OutputModel::Qt5OutputModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +int Qt5OutputModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_screens.size(); +} + +QString Qt5OutputModel::screenName(const QScreen *screen) +{ + if (!screen) { + return QString(); + } + + const QString name = screen->name(); + return name.isEmpty() ? QStringLiteral("Display") : name; +} + +Output::Rotation Qt5OutputModel::rotationForScreen(const QScreen *screen) +{ + if (!screen) { + return Output::None; + } + + switch (screen->orientation()) { + case Qt::PortraitOrientation: + return Output::Right; + case Qt::InvertedPortraitOrientation: + return Output::Left; + case Qt::InvertedLandscapeOrientation: + return Output::Inverted; + case Qt::LandscapeOrientation: + case Qt::PrimaryOrientation: + default: + return Output::None; + } +} + +QVariant Qt5OutputModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= m_screens.size()) { + return QVariant(); + } + + const QScreen *screen = m_screens.at(index.row()); + if (!screen) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return screenName(screen); + case EnabledRole: + return true; + case InternalRole: + return screenName(screen).startsWith(QStringLiteral("eDP"), Qt::CaseInsensitive) + || screenName(screen).startsWith(QStringLiteral("LVDS"), Qt::CaseInsensitive); + case PrimaryRole: + return screen == QGuiApplication::primaryScreen(); + case SizeRole: + return screen->size(); + case PositionRole: + case NormalizedPositionRole: + return screen->geometry().topLeft(); + case AutoRotateRole: + case AutoRotateOnlyInTabletModeRole: + return false; + case RotationRole: + return static_cast(rotationForScreen(screen)); + case ScaleRole: + return screen->devicePixelRatio(); + case ResolutionIndexRole: + return 0; + case ResolutionsRole: + return QStringList(QStringLiteral("%1 × %2").arg(screen->size().width()).arg(screen->size().height())); + case RefreshRateIndexRole: + return 0; + case RefreshRatesRole: + return QStringList(QStringLiteral("%1 Hz").arg(qRound(screen->refreshRate()))); + case ReplicationSourceModelRole: + return QStringList(); + case ReplicationSourceIndexRole: + return -1; + case ReplicasModelRole: + return QVariantList(); + default: + return QVariant(); + } +} + +bool Qt5OutputModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_UNUSED(value) + + if (!index.isValid() || index.row() < 0 || index.row() >= m_screens.size()) { + return false; + } + + // QScreen exposes the active mode as read-only. Resolution, rotation and + // output enablement are intentionally left to the desktop's display + // service on modern KDE systems. + switch (role) { + case EnabledRole: + case RotationRole: + case ResolutionIndexRole: + case RefreshRateIndexRole: + case ScaleRole: + return false; + default: + return false; + } +} + +QHash Qt5OutputModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles[Qt::DisplayRole] = "display"; + roles[EnabledRole] = "enabled"; + roles[InternalRole] = "internal"; + roles[PrimaryRole] = "primary"; + roles[SizeRole] = "size"; + roles[PositionRole] = "position"; + roles[NormalizedPositionRole] = "normalizedPosition"; + roles[AutoRotateRole] = "autoRotate"; + roles[AutoRotateOnlyInTabletModeRole] = "autoRotateOnlyInTabletMode"; + roles[RotationRole] = "rotation"; + roles[ScaleRole] = "scale"; + roles[ResolutionIndexRole] = "resolutionIndex"; + roles[ResolutionsRole] = "resolutions"; + roles[RefreshRateIndexRole] = "refreshRateIndex"; + roles[RefreshRatesRole] = "refreshRates"; + roles[ReplicationSourceModelRole] = "replicationSourceModel"; + roles[ReplicationSourceIndexRole] = "replicationSourceIndex"; + roles[ReplicasModelRole] = "replicasModel"; + return roles; +} + +void Qt5OutputModel::setScreens(const QList &screens) +{ + beginResetModel(); + m_screens = screens; + endResetModel(); +} diff --git a/screen/qt5/outputmodel.h b/screen/qt5/outputmodel.h new file mode 100644 index 0000000..9f09327 --- /dev/null +++ b/screen/qt5/outputmodel.h @@ -0,0 +1,53 @@ +#pragma once + +#include "output.h" + +#include +#include + +class Qt5OutputModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum OutputRoles { + EnabledRole = Qt::UserRole + 1, + InternalRole, + PrimaryRole, + SizeRole, + PositionRole, + NormalizedPositionRole, + AutoRotateRole, + AutoRotateOnlyInTabletModeRole, + RotationRole, + ScaleRole, + ResolutionIndexRole, + ResolutionsRole, + RefreshRateIndexRole, + RefreshRatesRole, + ReplicationSourceModelRole, + ReplicationSourceIndexRole, + ReplicasModelRole + }; + + explicit Qt5OutputModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, + const QVariant &value, + int role = Qt::EditRole) override; + + void setScreens(const QList &screens); + +protected: + QHash roleNames() const override; + +private: + static Output::Rotation rotationForScreen(const QScreen *screen); + static QString screenName(const QScreen *screen); + + QList m_screens; +}; + diff --git a/screen/qt5/plugin.cpp b/screen/qt5/plugin.cpp new file mode 100644 index 0000000..84443ae --- /dev/null +++ b/screen/qt5/plugin.cpp @@ -0,0 +1,22 @@ +#include "output.h" +#include "screen.h" + +#include +#include + +class QmlPlugins : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char *uri) override + { + qmlRegisterType(uri, 1, 0, "Screen"); + qmlRegisterUncreatableType(uri, 1, 0, "Output", + QStringLiteral("Output is provided by Screen")); + } +}; + +#include "plugin.moc" + diff --git a/screen/qt5/screen.cpp b/screen/qt5/screen.cpp new file mode 100644 index 0000000..e541afd --- /dev/null +++ b/screen/qt5/screen.cpp @@ -0,0 +1,49 @@ +#include "screen.h" + +#include "outputmodel.h" + +#include + +Qt5Screen::Qt5Screen(QObject *parent) + : QObject(parent) + , m_outputModel(new Qt5OutputModel(this)) +{ + refreshScreens(); + + connect(qApp, &QGuiApplication::screenAdded, this, &Qt5Screen::connectScreen); + connect(qApp, &QGuiApplication::screenAdded, this, &Qt5Screen::refreshScreens); + connect(qApp, &QGuiApplication::screenRemoved, this, &Qt5Screen::refreshScreens); +} + +Qt5OutputModel *Qt5Screen::outputModel() const +{ + return m_outputModel; +} + +void Qt5Screen::connectScreen(QScreen *screen) +{ + if (!screen) { + return; + } + + connect(screen, &QScreen::geometryChanged, this, &Qt5Screen::refreshScreens, Qt::UniqueConnection); + connect(screen, &QScreen::orientationChanged, this, &Qt5Screen::refreshScreens, Qt::UniqueConnection); + connect(screen, &QScreen::physicalDotsPerInchChanged, this, &Qt5Screen::refreshScreens, Qt::UniqueConnection); +} + +void Qt5Screen::refreshScreens() +{ + const QList screens = QGuiApplication::screens(); + for (QScreen *screen : screens) { + connectScreen(screen); + } + m_outputModel->setScreens(screens); + emit outputModelChanged(); +} + +void Qt5Screen::save() +{ + // Display configuration is managed by KScreen/system settings on KDE6. + // QScreen is a read-only Qt API, so there is nothing to write here. +} + diff --git a/screen/qt5/screen.h b/screen/qt5/screen.h new file mode 100644 index 0000000..ed0057a --- /dev/null +++ b/screen/qt5/screen.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class Qt5OutputModel; +class QScreen; + +class Qt5Screen : public QObject +{ + Q_OBJECT + Q_PROPERTY(Qt5OutputModel *outputModel READ outputModel NOTIFY outputModelChanged) + +public: + explicit Qt5Screen(QObject *parent = nullptr); + + Qt5OutputModel *outputModel() const; + Q_INVOKABLE void save(); + +private slots: + void refreshScreens(); + void connectScreen(QScreen *screen); + +signals: + void outputModelChanged(); + +private: + Qt5OutputModel *m_outputModel; +}; +