diff --git a/src/compositor/CMakeLists.txt b/src/compositor/CMakeLists.txt index f617b719..7c0d7207 100644 --- a/src/compositor/CMakeLists.txt +++ b/src/compositor/CMakeLists.txt @@ -94,6 +94,7 @@ liri_add_module(AuroraCompositor LIBRARIES Qt6::GuiPrivate Liri::AuroraGlobalPrivate + Liri::AuroraPlatform PKGCONFIG_DEPENDENCIES Qt6Core Qt6Gui @@ -235,7 +236,8 @@ if (TARGET Qt6::Qml) URI Aurora.Compositor VERSION 1.0 GENERATE_PLUGIN_SOURCE - DEPENDENCIES QtQuick + DEPENDENCIES Qt.Quick + IMPORTS Aurora.Platform ) ecm_target_qml_sources(AuroraCompositor diff --git a/src/compositor/compositor_api/aurorawaylandoutput.cpp b/src/compositor/compositor_api/aurorawaylandoutput.cpp index 73621e45..a7d4f133 100644 --- a/src/compositor/compositor_api/aurorawaylandoutput.cpp +++ b/src/compositor/compositor_api/aurorawaylandoutput.cpp @@ -187,6 +187,64 @@ void WaylandOutputPrivate::handleWindowPixelSizeChanged() } } +WaylandOutput::Subpixel +WaylandOutputPrivate::convertSubpixel(const Aurora::Platform::Output::Subpixel &subpixel) +{ + switch (subpixel) { + case Aurora::Platform::Output::Subpixel::Unknown: + return WaylandOutput::SubpixelUnknown; + case Aurora::Platform::Output::Subpixel::None: + return WaylandOutput::SubpixelNone; + case Aurora::Platform::Output::Subpixel::HorizontalRGB: + return WaylandOutput::SubpixelHorizontalRgb; + case Aurora::Platform::Output::Subpixel::HorizontalBGR: + return WaylandOutput::SubpixelHorizontalBgr; + case Aurora::Platform::Output::Subpixel::VerticalRGB: + return WaylandOutput::SubpixelVerticalRgb; + case Aurora::Platform::Output::Subpixel::VerticalBGR: + return WaylandOutput::SubpixelVerticalBgr; + } +} + +WaylandOutput::Transform +WaylandOutputPrivate::convertTransform(const Aurora::Platform::Output::Transform &transform) +{ + switch (transform) { + case Aurora::Platform::Output::Transform::Normal: + return WaylandOutput::TransformNormal; + case Aurora::Platform::Output::Transform::Rotated90: + return WaylandOutput::Transform90; + case Aurora::Platform::Output::Transform::Rotated180: + return WaylandOutput::Transform180; + case Aurora::Platform::Output::Transform::Rotated270: + return WaylandOutput::Transform270; + case Aurora::Platform::Output::Transform::Flipped: + return WaylandOutput::TransformFlipped; + case Aurora::Platform::Output::Transform::Flipped90: + return WaylandOutput::TransformFlipped90; + case Aurora::Platform::Output::Transform::Flipped180: + return WaylandOutput::TransformFlipped180; + case Aurora::Platform::Output::Transform::Flipped270: + return WaylandOutput::TransformFlipped270; + } +} + +void WaylandOutputPrivate::addModesFromPlatformOutput() +{ + Q_Q(WaylandOutput); + + if (platformOutput) { + const auto modes = platformOutput->modes(); + for (const auto &mode : modes) { + WaylandOutputMode waylandMode(mode.size, mode.refreshRate); + q->addMode(waylandMode); + + if (mode.flags.testFlag(Aurora::Platform::Output::Mode::Flag::Current)) + q->setCurrentMode(waylandMode); + } + } +} + void WaylandOutputPrivate::addView(WaylandView *view, WaylandSurface *surface) { for (int i = 0; i < surfaceViews.size(); i++) { @@ -928,6 +986,64 @@ void WaylandOutput::sendFrameCallbacks() wl_display_flush_clients(d->compositor->display()); } +Aurora::Platform::Output *WaylandOutput::platformOutput() const +{ + Q_D(const WaylandOutput); + return d->platformOutput; +} + +void WaylandOutput::setPlatformOutput(Aurora::Platform::Output *platformOutput) +{ + Q_D(WaylandOutput); + + if (d->platformOutput == platformOutput) + return; + + if (d->initialized) { + qWarning("Setting PlatformOutput %p on WaylandOutput %p is not " + "supported after WaylandOutput has been initialized", + static_cast(platformOutput), static_cast(this)); + return; + } + + d->platformOutput = platformOutput; + Q_EMIT platformOutputChanged(); + + if (platformOutput) { + if (d->sizeFollowsWindow) { + setSizeFollowsWindow(false); + qWarning("WaylandOutput is directly retrieving information from the " + "underlying platform: size follows window has been disabled"); + } + + setPosition(d->platformOutput->globalPosition()); + setManufacturer(d->platformOutput->manufacturer()); + setModel(d->platformOutput->model()); + setPhysicalSize(d->platformOutput->physicalSize()); + setSubpixel(d->convertSubpixel(d->platformOutput->subpixel())); + setTransform(d->convertTransform(d->platformOutput->transform())); + setScaleFactor(d->platformOutput->scale()); + + d->addModesFromPlatformOutput(); + + connect(d->platformOutput, &Aurora::Platform::Output::modeAdded, this, + [this](const Aurora::Platform::Output::Mode &mode) { + WaylandOutputMode waylandMode(mode.size, mode.refreshRate); + addMode(waylandMode); + + if (mode.flags.testFlag(Aurora::Platform::Output::Mode::Flag::Current)) + setCurrentMode(waylandMode); + }); + connect(d->platformOutput, &Aurora::Platform::Output::modeChanged, this, + [this](const Aurora::Platform::Output::Mode &mode) { + WaylandOutputMode waylandMode(mode.size, mode.refreshRate); + setCurrentMode(waylandMode); + }); + } else { + disconnect(d->platformOutput, nullptr, this, nullptr); + } +} + /*! * \internal */ diff --git a/src/compositor/compositor_api/aurorawaylandoutput.h b/src/compositor/compositor_api/aurorawaylandoutput.h index 17245675..28dc848c 100644 --- a/src/compositor/compositor_api/aurorawaylandoutput.h +++ b/src/compositor/compositor_api/aurorawaylandoutput.h @@ -11,6 +11,8 @@ #include #include +#include + struct wl_resource; class QWindow; @@ -41,6 +43,7 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutput : public WaylandObject Q_PROPERTY(Aurora::Compositor::WaylandOutput::Transform transform READ transform WRITE setTransform NOTIFY transformChanged) Q_PROPERTY(int scaleFactor READ scaleFactor WRITE setScaleFactor NOTIFY scaleFactorChanged) Q_PROPERTY(bool sizeFollowsWindow READ sizeFollowsWindow WRITE setSizeFollowsWindow NOTIFY sizeFollowsWindowChanged) + Q_PROPERTY(Aurora::Platform::Output *platformOutput READ platformOutput WRITE setPlatformOutput NOTIFY platformOutputChanged) QML_NAMED_ELEMENT(WaylandOutputBase) QML_ADDED_IN_VERSION(1, 0) @@ -120,6 +123,9 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutput : public WaylandObject bool physicalSizeFollowsSize() const; void setPhysicalSizeFollowsSize(bool follow); + Aurora::Platform::Output *platformOutput() const; + void setPlatformOutput(Aurora::Platform::Output *platformOutput); + void frameStarted(); void sendFrameCallbacks(); @@ -145,6 +151,7 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutput : public WaylandObject void manufacturerChanged(); void modelChanged(); void windowDestroyed(); + void platformOutputChanged(); protected: bool event(QEvent *event) override; diff --git a/src/compositor/compositor_api/aurorawaylandoutput_p.h b/src/compositor/compositor_api/aurorawaylandoutput_p.h index 50aa8abb..ad720d1a 100644 --- a/src/compositor/compositor_api/aurorawaylandoutput_p.h +++ b/src/compositor/compositor_api/aurorawaylandoutput_p.h @@ -79,6 +79,10 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutputPrivate : public QObjectPrivate, void handleWindowPixelSizeChanged(); + WaylandOutput::Subpixel convertSubpixel(const Aurora::Platform::Output::Subpixel &subpixel); + WaylandOutput::Transform convertTransform(const Aurora::Platform::Output::Transform &transform); + void addModesFromPlatformOutput(); + QPointer xdgOutput; protected: @@ -105,6 +109,7 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutputPrivate : public QObjectPrivate, bool sizeFollowsWindow = false; bool initialized = false; QSize windowPixelSize; + Aurora::Platform::Output *platformOutput = nullptr; Q_DISABLE_COPY(WaylandOutputPrivate) @@ -115,4 +120,3 @@ class LIRIAURORACOMPOSITOR_EXPORT WaylandOutputPrivate : public QObjectPrivate, } // namespace Compositor } // namespace Aurora - diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index cf668e92..0ce91aa4 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -24,6 +24,7 @@ liri_add_module(AuroraPlatform inputmanager.cpp inputmanager.h keyboarddevice.cpp keyboarddevice.h keyboarddevice_p.h output.cpp output.h output_p.h + outputsmodel.cpp outputsmodel.h outputsmodel_p.h pointerdevice.cpp pointerdevice.h session.cpp session.h session_noop.cpp session_noop_p.h @@ -39,6 +40,13 @@ liri_add_module(AuroraPlatform Qt6::Core Qt6::Gui Liri::AuroraCore + Qt6::Qml ) - liri_finalize_module(AuroraPlatform) + +ecm_add_qml_module(AuroraPlatform + URI Aurora.Platform + VERSION 1.0 + GENERATE_PLUGIN_SOURCE +) +ecm_finalize_qml_module(AuroraPlatform) diff --git a/src/platform/output.h b/src/platform/output.h index 9828fd14..2cb86ff4 100644 --- a/src/platform/output.h +++ b/src/platform/output.h @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -22,6 +23,8 @@ class OutputPrivate; class LIRIAURORAPLATFORM_EXPORT Output : public QObject { Q_OBJECT + QML_NAMED_ELEMENT(PlatformOutput) + QML_UNCREATABLE("Cannot instantiate PlatformOutput") Q_PROPERTY(QUuid uuid READ uuid CONSTANT) Q_PROPERTY(QScreen *screen READ screen NOTIFY screenChanged) Q_PROPERTY(QString name READ name CONSTANT) diff --git a/src/platform/outputsmodel.cpp b/src/platform/outputsmodel.cpp new file mode 100644 index 00000000..e08a5ac9 --- /dev/null +++ b/src/platform/outputsmodel.cpp @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: 2024 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "eglfsdeviceintegration_p.h" +#include "outputsmodel.h" +#include "outputsmodel_p.h" + +namespace Aurora { + +namespace Platform { + +/* + * PlatformOutputsModel + */ + +OutputsModel::OutputsModel(QObject *parent) + : QAbstractListModel(parent) + , d_ptr(new OutputsModelPrivate(this)) +{ + Q_D(OutputsModel); + + auto *deviceIntegration = auroraDeviceIntegration(); + d->outputs = deviceIntegration->outputs(); + + connect(deviceIntegration, &DeviceIntegration::outputAdded, this, [this, d](Output *output) { + beginInsertRows(QModelIndex(), d->outputs.size(), d->outputs.size()); + d->outputs.append(output); + endInsertRows(); + }); + connect(deviceIntegration, &DeviceIntegration::outputRemoved, this, [this, d](Output *output) { + beginRemoveRows(QModelIndex(), d->outputs.size(), d->outputs.size()); + d->outputs.removeOne(output); + endRemoveRows(); + }); +} + +OutputsModel::~OutputsModel() +{ +} + +QHash OutputsModel::roleNames() const +{ + QHash roles; + roles[OutputRole] = "output"; + return roles; +} + +int OutputsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + Q_D(const OutputsModel); + return d->outputs.size(); +} + +QVariant OutputsModel::data(const QModelIndex &index, int role) const +{ + Q_D(const OutputsModel); + + if (index.row() < 0 || index.row() >= d->outputs.size()) + return {}; + + auto *output = d->outputs.at(index.row()); + if (!output) + return {}; + + switch (role) { + case OutputRole: + return QVariant::fromValue(output); + default: + break; + } + + return {}; +} + +/* + * OutputsModelPrivate + */ + +OutputsModelPrivate::OutputsModelPrivate(OutputsModel *self) + : q_ptr(self) +{ +} + +} // namespace Platform + +} // namespace Aurora diff --git a/src/platform/outputsmodel.h b/src/platform/outputsmodel.h new file mode 100644 index 00000000..b8d4e072 --- /dev/null +++ b/src/platform/outputsmodel.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2024 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include + +namespace Aurora { + +namespace Platform { + +class OutputsModelPrivate; + +class LIRIAURORAPLATFORM_EXPORT OutputsModel : public QAbstractListModel +{ + Q_OBJECT + QML_NAMED_ELEMENT(PlatformOutputsModel) + Q_DECLARE_PRIVATE(OutputsModel) +public: + enum OutputRoles { + OutputRole = Qt::UserRole + 1 + }; + + explicit OutputsModel(QObject *parent = nullptr); + ~OutputsModel(); + + QHash roleNames() const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + QScopedPointer const d_ptr; +}; + +} // namespace Platform + +} // namespace Aurora diff --git a/src/platform/outputsmodel_p.h b/src/platform/outputsmodel_p.h new file mode 100644 index 00000000..c0318a54 --- /dev/null +++ b/src/platform/outputsmodel_p.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Aurora API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace Aurora { + +namespace Platform { + +class LIRIAURORAPLATFORM_EXPORT OutputsModelPrivate +{ + Q_DECLARE_PUBLIC(OutputsModel) +public: + OutputsModelPrivate(OutputsModel *self); + + QList outputs; + +protected: + OutputsModel *q_ptr = nullptr; +}; + +} // namespace Platform + +} // namespace Aurora