From 3b3088370c962c53c10aaaff48ea50fb28d0f191 Mon Sep 17 00:00:00 2001 From: Pier Luigi Fiorini Date: Sat, 20 Jan 2024 09:15:28 +0100 Subject: [PATCH] Expose platform outputs to compositors Introduce OutputsModel that together with Output can be used from QML to access platform outputs. Compositors will use it to create WaylandOutput instrances for each platform output. Closes: #47 --- src/compositor/CMakeLists.txt | 4 +- .../compositor_api/aurorawaylandoutput.cpp | 116 ++++++++++++++++++ .../compositor_api/aurorawaylandoutput.h | 7 ++ .../compositor_api/aurorawaylandoutput_p.h | 6 +- src/platform/CMakeLists.txt | 10 +- src/platform/output.h | 3 + src/platform/outputsmodel.cpp | 87 +++++++++++++ src/platform/outputsmodel.h | 41 +++++++ src/platform/outputsmodel_p.h | 38 ++++++ 9 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 src/platform/outputsmodel.cpp create mode 100644 src/platform/outputsmodel.h create mode 100644 src/platform/outputsmodel_p.h 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 ca09b0eb..9dfe278e 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -23,6 +23,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 @@ -36,6 +37,13 @@ liri_add_module(AuroraPlatform PUBLIC_LIBRARIES Qt6::Core Qt6::Gui + 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 6e461b21..c3b30eaf 100644 --- a/src/platform/output.h +++ b/src/platform/output.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -21,6 +22,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