diff --git a/CMakeLists.txt b/CMakeLists.txt index 043ac7b7..a738d45d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ endif() if(FEATURE_aurora_xkbcommon) add_subdirectory(src/platformsupport/xkbcommon) endif() +add_subdirectory(src/core) add_subdirectory(src/compositor) if(FEATURE_aurora_brcm) add_subdirectory(src/plugins/hardwareintegration/compositor/brcm-egl) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 00000000..5a3acad6 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +# SPDX-License-Identifier: BSD-3-Clause + +include(ECMQtDeclareLoggingCategory) +ecm_qt_declare_logging_category( + AuroraCore_SOURCES + HEADER "auroracoreloggingcategories.h" + IDENTIFIER "Aurora::Core::gLcAuroraCore" + CATEGORY_NAME "aurora.core" + DEFAULT_SEVERITY "Info" + DESCRIPTION "Aurora platform abstraction" +) + +liri_add_module(AuroraCore + DESCRIPTION + "Aurora core library" + SOURCES + deviceintegration.cpp deviceintegration.h + deviceintegrationplugin.cpp deviceintegrationplugin.h + eglconfigchooser.cpp eglconfigchooser_p.h + inputdevice.cpp inputdevice.h + inputmanager.cpp inputmanager.h + keyboarddevice.cpp keyboarddevice.h + output.cpp output.h output_p.h + session.cpp session.h + session_noop.cpp session_noop_p.h + window.cpp window.h + ${AuroraCore_SOURCES} + DEFINES + QT_NO_CAST_FROM_ASCII + PUBLIC_LIBRARIES + Qt5::Core + Qt5::Gui +) + +liri_finalize_module(AuroraCore) diff --git a/src/core/deviceintegration.cpp b/src/core/deviceintegration.cpp new file mode 100644 index 00000000..e44a3055 --- /dev/null +++ b/src/core/deviceintegration.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "deviceintegration.h" +#include "deviceintegration_p.h" +#include "eglconfigchooser_p.h" +#include "inputmanager.h" + +namespace Aurora { + +namespace Core { + +/* + * DeviceIntegration + */ + +DeviceIntegration::DeviceIntegration(QObject *parent) + : QObject(parent), d_ptr(new DeviceIntegrationPrivate(this)) +{ +} + +DeviceIntegration::~DeviceIntegration() { } + +bool DeviceIntegration::isReady() const +{ + Q_D(const DeviceIntegration); + return d->ready; +} + +bool DeviceIntegration::supportsPBuffers() +{ + return true; +} + +bool DeviceIntegration::supportsSurfacelessContexts() +{ + return true; +} + +EGLNativeDisplayType DeviceIntegration::platformDisplay() const +{ + return EGL_DEFAULT_DISPLAY; +} + +EGLDisplay DeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) +{ + return eglGetDisplay(nativeDisplay); +} + +void DeviceIntegration::destroyDisplay(EGLDisplay eglDisplay) +{ + if (eglDisplay != EGL_NO_DISPLAY) + eglTerminate(eglDisplay); +} + +EGLNativeWindowType DeviceIntegration::createNativeWindow(Window *window, const QSize &size, + const QSurfaceFormat &format) +{ + Q_UNUSED(window) + Q_UNUSED(size) + Q_UNUSED(format) + return 0; +} + +void DeviceIntegration::destroyNativeWindow(EGLNativeWindowType nativeWindow) +{ + Q_UNUSED(nativeWindow) +} + +QSurfaceFormat DeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + return inputFormat; +} + +EGLint DeviceIntegration::surfaceType() const +{ + return EGL_WINDOW_BIT; +} + +EGLConfig DeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) +{ + Q_D(DeviceIntegration); + + QVector configAttribs = eglConfigAttributesFromSurfaceFormat(display, format); + + configAttribs.append(EGL_SURFACE_TYPE); + configAttribs.append(surfaceType()); + + configAttribs.append(EGL_NONE); + + // Get the number of matching configurations for the attributes + EGLConfig config = nullptr; + EGLint numConfigs = 0; + if (!eglChooseConfig(display, configAttribs.constData(), &config, 1, &numConfigs)) + return nullptr; + return config; +} + +InputManager *Aurora::Core::DeviceIntegration::createInputManager() +{ + return nullptr; +} + +void DeviceIntegration::setReady(bool ready) +{ + Q_D(DeviceIntegration); + + if (d->ready == ready) + return; + + d->ready = ready; + emit readyChanged(ready); +} + +/* + * DeviceIntegrationPrivate + */ + +DeviceIntegrationPrivate::DeviceIntegrationPrivate(DeviceIntegration *self) : q_ptr(self) { } + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/deviceintegration.h b/src/core/deviceintegration.h new file mode 100644 index 00000000..09012a7b --- /dev/null +++ b/src/core/deviceintegration.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include + +#include + +#include + +class QPlatformSurface; + +namespace Aurora { + +namespace Core { + +class DeviceIntegrationPrivate; +class InputManager; +class Window; + +class LIRIAURORACORE_EXPORT DeviceIntegration : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged) + Q_DECLARE_PRIVATE(DeviceIntegration) +public: + ~DeviceIntegration(); + + bool isReady() const; + + virtual void initialize() = 0; + virtual void destroy() = 0; + + virtual bool supportsPBuffers(); + virtual bool supportsSurfacelessContexts(); + + virtual EGLNativeDisplayType platformDisplay() const; + + virtual EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay); + virtual void destroyDisplay(EGLDisplay eglDisplay); + + virtual EGLNativeWindowType createNativeWindow(Window *window, const QSize &size, + const QSurfaceFormat &format); + virtual void destroyNativeWindow(EGLNativeWindowType nativeWindow); + + virtual QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; + virtual EGLint surfaceType() const; + + virtual EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); + + virtual Window *createWindow(QWindow *window) = 0; + + virtual void waitForVSync(Window *window) const = 0; + virtual void presentBuffer(Window *window) = 0; + + virtual InputManager *createInputManager(); + + virtual Outputs outputs() const = 0; + +signals: + void readyChanged(bool ready); + void outputAdded(Output *output); + void outputRemoved(Output *output); + +protected: + QScopedPointer const d_ptr; + + explicit DeviceIntegration(QObject *parent = nullptr); + + void setReady(bool ready); +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/deviceintegration_p.h b/src/core/deviceintegration_p.h new file mode 100644 index 00000000..ecc18609 --- /dev/null +++ b/src/core/deviceintegration_p.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#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 Core { + +class LIRIAURORACORE_EXPORT DeviceIntegrationPrivate +{ + Q_DECLARE_PUBLIC(DeviceIntegration) +public: + DeviceIntegrationPrivate(DeviceIntegration *self); + + bool ready = false; + +protected: + DeviceIntegration *q_ptr = nullptr; +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/deviceintegrationplugin.cpp b/src/core/deviceintegrationplugin.cpp new file mode 100644 index 00000000..28404a2a --- /dev/null +++ b/src/core/deviceintegrationplugin.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include + +#include "auroracoreloggingcategories.h" +#include "deviceintegrationplugin.h" + +namespace Aurora { + +namespace Core { + +DeviceIntegrationPlugin::DeviceIntegrationPlugin(QObject *parent) : QObject(parent) { } + +QStringList DeviceIntegrationFactory::keys(const QString &pluginPath) +{ + QStringList list; + + if (!pluginPath.isEmpty()) + QCoreApplication::addLibraryPath(pluginPath); + + const auto paths = QCoreApplication::libraryPaths(); + for (const auto &path : paths) { + const auto absolutePath = + QDir(path).absoluteFilePath(QStringLiteral("aurora/deviceintegration")); + QDir dir(absolutePath); + + const auto fileNames = dir.entryList(QDir::Files); + for (const auto &fileName : fileNames) { + QPluginLoader loader(dir.absoluteFilePath(fileName)); + + if (loader.load()) { + const auto metaData = + loader.metaData().value(QLatin1String("MetaData")).toVariant().toMap(); + list += metaData.value(QStringLiteral("Keys"), QStringList()).toStringList(); + } + + loader.unload(); + } + } + + qCDebug(gLcAuroraCore) << "EGL device integration plugin keys:" << list; + return list; +} + +DeviceIntegration *DeviceIntegrationFactory::create(const QString &name, const QString &pluginPath) +{ + if (!pluginPath.isEmpty()) + QCoreApplication::addLibraryPath(pluginPath); + + const auto paths = QCoreApplication::libraryPaths(); + for (const auto &path : paths) { + const auto absolutePath = + QDir(path).absoluteFilePath(QStringLiteral("aurora/deviceintegration")); + QDir dir(absolutePath); + + const auto fileNames = dir.entryList(QDir::Files); + for (const auto &fileName : fileNames) { + QPluginLoader loader(dir.absoluteFilePath(fileName)); + + if (loader.load()) { + const auto metaData = + loader.metaData().value(QLatin1String("MetaData")).toVariant().toMap(); + const auto keys = + metaData.value(QStringLiteral("Keys"), QStringList()).toStringList(); + + if (keys.contains(name)) { + auto *plugin = dynamic_cast(loader.instance()); + if (plugin) + return plugin->create(); + } + } + + loader.unload(); + } + } + + return nullptr; +} + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/deviceintegrationplugin.h b/src/core/deviceintegrationplugin.h new file mode 100644 index 00000000..8cbaca11 --- /dev/null +++ b/src/core/deviceintegrationplugin.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include + +namespace Aurora { + +namespace Core { + +class DeviceIntegration; + +class LIRIAURORACORE_EXPORT DeviceIntegrationPlugin : public QObject +{ + Q_OBJECT +public: + explicit DeviceIntegrationPlugin(QObject *parent = nullptr); + + virtual DeviceIntegration *create() = 0; +}; + +class LIRIAURORACORE_EXPORT DeviceIntegrationFactory +{ +public: + static QStringList keys(const QString &pluginPath = QString()); + static DeviceIntegration *create(const QString &name, const QString &pluginPath = QString()); +}; + +} // namespace Core + +} // namespace Aurora + +Q_DECLARE_INTERFACE(Aurora::Core::DeviceIntegrationPlugin, + "io.liri.Aurora.DeviceIntegrationPlugin/1") diff --git a/src/core/eglconfigchooser.cpp b/src/core/eglconfigchooser.cpp new file mode 100644 index 00000000..e6e71553 --- /dev/null +++ b/src/core/eglconfigchooser.cpp @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "eglconfigchooser_p.h" + +#ifndef EGL_OPENGL_ES3_BIT_KHR +# define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +namespace Aurora { + +namespace Core { + +bool hasEglExtension(EGLDisplay display, const char *name) +{ + QList extensions = + QByteArray(reinterpret_cast(eglQueryString(display, EGL_EXTENSIONS))) + .split(' '); + return extensions.contains(name); +} + +QVector eglConfigAttributesFromSurfaceFormat(EGLDisplay display, + const QSurfaceFormat &format) +{ + QVector configAttribs; + + configAttribs.append(EGL_RED_SIZE); + configAttribs.append(qMax(0, format.redBufferSize())); + + configAttribs.append(EGL_GREEN_SIZE); + configAttribs.append(qMax(0, format.greenBufferSize())); + + configAttribs.append(EGL_BLUE_SIZE); + configAttribs.append(qMax(0, format.blueBufferSize())); + + configAttribs.append(EGL_ALPHA_SIZE); + configAttribs.append(qMax(0, format.alphaBufferSize())); + + configAttribs.append(EGL_SAMPLES); + configAttribs.append(qMax(0, format.samples())); + + configAttribs.append(EGL_SAMPLE_BUFFERS); + configAttribs.append(format.samples() > 0 ? 1 : 0); + + switch (format.renderableType()) { + case QSurfaceFormat::OpenGL: + configAttribs.append(EGL_RENDERABLE_TYPE); + configAttribs.append(EGL_OPENGL_BIT); + break; + case QSurfaceFormat::OpenGLES: + configAttribs.append(EGL_RENDERABLE_TYPE); + if (format.majorVersion() == 1) + configAttribs.append(EGL_OPENGL_ES_BIT); + else if (format.majorVersion() == 2) + configAttribs.append(EGL_OPENGL_ES2_BIT); + else if (format.majorVersion() == 3 && hasEglExtension(display, "EGL_KHR_create_context")) + configAttribs.append(EGL_OPENGL_ES3_BIT_KHR); + else if (format.majorVersion() == 3) + configAttribs.append(EGL_OPENGL_ES3_BIT); + break; + case QSurfaceFormat::OpenVG: + configAttribs.append(EGL_RENDERABLE_TYPE); + configAttribs.append(EGL_OPENVG_BIT); + break; + default: + break; + } + + if (format.renderableType() != QSurfaceFormat::OpenVG) { + configAttribs.append(EGL_DEPTH_SIZE); + configAttribs.append(qMax(0, format.depthBufferSize())); + + configAttribs.append(EGL_STENCIL_SIZE); + configAttribs.append(qMax(0, format.stencilBufferSize())); + } else { + configAttribs.append(EGL_ALPHA_MASK_SIZE); + configAttribs.append(8); + } + + return configAttribs; +} + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/eglconfigchooser_p.h b/src/core/eglconfigchooser_p.h new file mode 100644 index 00000000..afe7cffb --- /dev/null +++ b/src/core/eglconfigchooser_p.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include + +namespace Aurora { + +namespace Core { + +bool hasEglExtension(EGLDisplay display, const char *name); +QVector eglConfigAttributesFromSurfaceFormat(EGLDisplay display, + const QSurfaceFormat &format); + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/inputdevice.cpp b/src/core/inputdevice.cpp new file mode 100644 index 00000000..2dd08652 --- /dev/null +++ b/src/core/inputdevice.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "inputdevice.h" + +namespace Aurora { + +namespace Core { + +InputDevice::InputDevice(QObject *parent) : QObject(parent) { } + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/inputdevice.h b/src/core/inputdevice.h new file mode 100644 index 00000000..e64561ed --- /dev/null +++ b/src/core/inputdevice.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT InputDevice : public QObject +{ + Q_OBJECT +public: + explicit InputDevice(QObject *parent = nullptr); +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/inputmanager.cpp b/src/core/inputmanager.cpp new file mode 100644 index 00000000..185051fd --- /dev/null +++ b/src/core/inputmanager.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "inputmanager.h" + +namespace Aurora { + +namespace Core { + +InputManager::InputManager(QObject *parent) : QObject(parent) { } + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/inputmanager.h b/src/core/inputmanager.h new file mode 100644 index 00000000..a69c9cb3 --- /dev/null +++ b/src/core/inputmanager.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT InputManager : public QObject +{ + Q_OBJECT +public: + explicit InputManager(QObject *parent = nullptr); +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/keyboarddevice.cpp b/src/core/keyboarddevice.cpp new file mode 100644 index 00000000..3afafe8b --- /dev/null +++ b/src/core/keyboarddevice.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "keyboarddevice.h" + +namespace Aurora { + +namespace Core { + +KeyboardDevice::KeyboardDevice(QObject *parent) : InputDevice(parent) { } + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/keyboarddevice.h b/src/core/keyboarddevice.h new file mode 100644 index 00000000..f079c694 --- /dev/null +++ b/src/core/keyboarddevice.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT KeyboardDevice : public InputDevice +{ + Q_OBJECT +public: + explicit KeyboardDevice(QObject *parent = nullptr); +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/output.cpp b/src/core/output.cpp new file mode 100644 index 00000000..e1803b17 --- /dev/null +++ b/src/core/output.cpp @@ -0,0 +1,446 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "output.h" +#include "output_p.h" + +namespace Aurora { + +namespace Core { + +/*! + \class Output + \inmodule AuroraCore + \brief Generic output representation. + + The Output class represents an output. + */ + +/*! + * Constructs an Output with the given \a parent. + */ +Output::Output(QObject *parent) : QObject(parent), d_ptr(new OutputPrivate(this)) { } + +Output::~Output() { } + +/*! + * \property Output::name + * \brief A user presentable string representing the output. + * + * This property contains a user presentable string representing the output, + * typycally something like "VGA1", "eDP-1", "HDMI1", etc. + */ +QString Output::name() const +{ + Q_D(const Output); + return d->name; +} + +/*! + * \property Output::description + * \brief Human readable description of the output. + * + * This property contains a human readable description of he output. + */ +QString Output::description() const +{ + Q_D(const Output); + return d->description; +} + +/*! + * \property Output::uuid + * \brief The unique identifier of the output. + * + * This property contains a unique identifier of the output. + */ +QUuid Output::uuid() const +{ + Q_D(const Output); + return d->uuid; +} + +/*! + * \property Output::manufacturer + * \brief The manufacturer of the screen. + * + * This property holds the manufacturer of the screen. + */ +QString Output::manufacturer() const +{ + Q_D(const Output); + return d->manufacturer; +} + +/*! + * \property Output::model + * \brief The model of the screen. + * + * This property holds the model of the screen. + */ +QString Output::model() const +{ + Q_D(const Output); + return d->model; +} + +/*! + * \property Output::serialNumber + * \brief The serial number of the screen. + * + * This property holds the serial number of the screen. + */ +QString Output::serialNumber() const +{ + Q_D(const Output); + return d->serialNumber; +} + +/*! + * \property Output::physicalSize + * \brief The physical size of the screen in millimiters. + * + * This property holds the physical size of the screen in millimiters. + */ +QSize Output::physicalSize() const +{ + Q_D(const Output); + return d->physicalSize; +} + +/*! + * \property Output::enabled + * \brief Weather the output is enable or not. + * + * This property holds weather the output is enabled or not. + */ +bool Output::isEnabled() const +{ + Q_D(const Output); + return d->enabled; +} + +/*! + * \property Output::globalPosition + * \brief Position in the global compositor space. + * + * This property holds the output position within the global compositor space. + */ +QPoint Output::globalPosition() const +{ + Q_D(const Output); + return d->globalPosition; +} + +/*! + * \property Output::pixelSize + * \brief Size. + * + * This property holds the output size in pixels, taking transform into account. + * + * \sa Output::modeSize + * \sa Output::transform + */ +QSize Output::pixelSize() const +{ + Q_D(const Output); + + switch (d->transform) { + case Output::Transform::Rotated90: + case Output::Transform::Rotated270: + case Output::Transform::Flipped90: + case Output::Transform::Flipped270: + return modeSize().transposed(); + default: + break; + } + + return modeSize(); +} + +/*! + * \property Output::modeSize + * \brief Actual resolution. + * + * This property holds the actual resolution of the output, without + * being multiplied by the scale. + * + * \sa Output::pixelSize + * \sa Output::scale + */ +QSize Output::modeSize() const +{ + Q_D(const Output); + + if (d->currentMode == d->modes.end()) + return QSize(); + return d->currentMode->size; +} + +/*! + * \property Output::scale + * \brief Scale. + * + * This property holds the output scale. + */ +qreal Output::scale() const +{ + Q_D(const Output); + return d->scale; +} + +/*! + * \property Output::geometry + * \brief Geometry of the output. + * + * This property holds the position of the output in the compositor space + * and the size in pixels. + * + * The geometry is transformed according to the output transform. + * + * \sa Output::transform + * \sa Output::globalPosition + * \sa Output::pixelSize + * \sa Output::scale + */ +QRect Output::geometry() const +{ + Q_D(const Output); + + if (d->currentMode == d->modes.end()) + return QRect(); + + auto rect = QRect(d->globalPosition, pixelSize() / d->scale); + auto x = rect.x(); + auto y = rect.y(); + auto width = rect.width(); + auto height = rect.height(); + + switch (d->transform) { + case Output::Transform::Normal: + return rect; + case Output::Transform::Rotated90: + return QRect(y, rect.left(), height, width); + case Output::Transform::Rotated180: + return QRect(rect.topLeft(), QSize(width, height)); + case Output::Transform::Rotated270: + return QRect(rect.top(), x, height, width); + case Output::Transform::Flipped: + return QRect(x + width, y, -width, height); + case Output::Transform::Flipped90: + return QRect(y + height, rect.left(), -height, width); + case Output::Transform::Flipped180: + return QRect(rect.bottomRight(), QSize(-width, -height)); + case Output::Transform::Flipped270: + return QRect(rect.top(), x + width, height, -width); + } +} + +/*! + * \property Output::refreshRate + * \brief The refresh rate of the output in mHz. + * + * This property holds the refresh rate of the output in mHz. + */ +int Output::refreshRate() const +{ + Q_D(const Output); + + if (d->currentMode == d->modes.end()) + return 0; + return d->currentMode->refreshRate; +} + +/*! + * \property Output::depth + * \brief Color depth. + * + * This property holds the color depth of the output. + * It must be compatible with the image format: for example if the + * Output::format property is QImage::Format_RGB32, depth must be 32. + * + * \ sa Output::format + */ +int Output::depth() const +{ + Q_D(const Output); + return d->depth; +} + +/*! + * \property Output::format + * \brief Image format. + * + * This property holds the image format of the output. + * It must be compatible with color depth: for example if the + * Output::depth property is 32, format might be QImage::Format_RGB32. + * + * \sa Output::depth + */ +QImage::Format Output::format() const +{ + Q_D(const Output); + return d->format; +} + +/*! + * \property Output::powerState + * \brief The power state. + * + * This property holds the power state of the screen. + */ +Output::PowerState Output::powerState() const +{ + Q_D(const Output); + return d->powerState; +} + +void Output::setPowerState(Output::PowerState powerState) +{ + Q_D(Output); + + if (powerState == d->powerState) + return; + + d->powerState = powerState; + emit powerStateChanged(powerState); +} + +Output::Subpixel Output::subpixel() const +{ + Q_D(const Output); + return d->subpixel; +} + +Output::Transform Output::transform() const +{ + Q_D(const Output); + return d->transform; +} + +Output::ContentType Output::contentType() const +{ + Q_D(const Output); + return d->contentType; +} + +void Output::setContentType(Output::ContentType contentType) +{ + Q_D(Output); + + if (d->contentType == contentType) + return; + + d->contentType = contentType; + emit contentTypeChanged(contentType); +} + +QDebug operator<<(QDebug debug, const Output *output) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + + if (output) { + debug << output->metaObject()->className() << '(' << static_cast(output); + debug << ", name=" << output->name(); + debug << ", geometry=" << output->geometry(); + // scale + if (debug.verbosity() > 2) { + debug << ", manufacturer=" << output->manufacturer(); + debug << ", model=" << output->model(); + debug << ", serialNumber=" << output->serialNumber(); + } + debug << ')'; + } else { + debug << "Output(0x0)"; + } + + return debug; +} + +bool Output::Mode::operator==(const Output::Mode &m) const +{ + return flags == m.flags && size == m.size && refreshRate == m.refreshRate; +} + +/* + * OutputPrivate + */ + +OutputPrivate::OutputPrivate(Output *self) : q_ptr(self) { } + +void OutputPrivate::setManufacturer(const QString &manufacturer) +{ + Q_Q(Output); + + if (this->manufacturer != manufacturer) { + this->manufacturer = manufacturer; + emit q->manufacturerChanged(manufacturer); + } +} + +void OutputPrivate::setModel(const QString &model) +{ + Q_Q(Output); + + if (this->model != model) { + this->model = model; + emit q->modelChanged(model); + } +} + +void OutputPrivate::setSubpixel(Output::Subpixel subpixel) +{ + Q_Q(Output); + + if (this->subpixel != subpixel) { + this->subpixel = subpixel; + emit q->subpixelChanged(subpixel); + } +} + +void OutputPrivate::setTransform(Output::Transform transform) +{ + Q_Q(Output); + + if (this->transform != transform) { + this->transform = transform; + emit q->transformChanged(transform); + } +} + +void OutputPrivate::setPhysicalSize(const QSize &physicalSize) +{ + Q_Q(Output); + + if (this->physicalSize != physicalSize) { + this->physicalSize = physicalSize; + emit q->physicalSizeChanged(physicalSize); + } +} + +void OutputPrivate::setGlobalPosition(const QPoint &globalPosition) +{ + Q_Q(Output); + + if (this->globalPosition != globalPosition) { + this->globalPosition = globalPosition; + emit q->globalPositionChanged(globalPosition); + } +} + +void OutputPrivate::setScale(qreal scale) +{ + Q_Q(Output); + + if (this->scale != scale) { + this->scale = scale; + emit q->scaleChanged(scale); + } +} + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/output.h b/src/core/output.h new file mode 100644 index 00000000..4ec473b3 --- /dev/null +++ b/src/core/output.h @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace Aurora { + +namespace Core { + +class OutputPrivate; + +class LIRIAURORACORE_EXPORT Output : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUuid uuid READ uuid CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) + Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged) + Q_PROPERTY(QString model READ model NOTIFY modelChanged) + Q_PROPERTY(QString serialNumber READ serialNumber CONSTANT) + Q_PROPERTY(QSize physicalSize READ physicalSize NOTIFY physicalSizeChanged) + Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) + Q_PROPERTY(QPoint globalPosition READ globalPosition NOTIFY globalPositionChanged) + Q_PROPERTY(QSize modeSize READ modeSize NOTIFY modeSizeChanged) + Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged) + Q_PROPERTY(qreal scale READ scale NOTIFY scaleChanged) + Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) + Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged) + Q_PROPERTY(int depth READ depth CONSTANT) + Q_PROPERTY(int format READ format CONSTANT) + Q_PROPERTY(PowerState powerState READ powerState WRITE setPowerState NOTIFY powerStateChanged) + Q_PROPERTY(Subpixel subpixel READ subpixel NOTIFY subpixelChanged) + Q_PROPERTY(Transform transform READ transform NOTIFY transformChanged) + Q_PROPERTY( + ContentType contenType READ contentType WRITE setContentType NOTIFY contentTypeChanged) + Q_DECLARE_PRIVATE(Output) +public: + Q_DISABLE_COPY_MOVE(Output) + + enum class Capability : uint { + PowerState = 1, + Overscan = 1 << 1, + Vrr = 1 << 2, + RgbRange = 1 << 3, + HighDynamicRange = 1 << 4, + WideColorGamut = 1 << 5, + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + enum class PowerState { + On, + Standby, + Suspend, + Off, + }; + Q_ENUM(PowerState) + + enum class Subpixel { + Unknown, + None, + HorizontalRGB, + HorizontalBGR, + VerticalRGB, + VerticalBGR, + }; + Q_ENUM(Subpixel) + + enum class Transform { + Normal, + Rotated90, + Rotated180, + Rotated270, + Flipped, + Flipped90, + Flipped180, + Flipped270, + }; + Q_ENUM(Transform); + + enum class ContentType { + Unknown, + Photo, + Video, + Game, + }; + Q_ENUM(ContentType) + + struct Mode + { + enum class Flag { + None = 0, + Current = 1 << 0, + Preferred = 1 << 1, + }; + Q_DECLARE_FLAGS(Flags, Flag) + + /*! Weather this mode is current or preferred */ + Flags flags = Flag::None; + + /*! Size in pixel space */ + QSize size; + + /*! Refresh rate in mHz */ + int refreshRate = 0; + + bool operator==(const Mode &m) const; + }; + + explicit Output(QObject *parent = nullptr); + ~Output(); + + QUuid uuid() const; + + QString name() const; + QString description() const; + + QString manufacturer() const; + QString model() const; + QString serialNumber() const; + + QSize physicalSize() const; + + bool isEnabled() const; + + QPoint globalPosition() const; + QSize pixelSize() const; + QSize modeSize() const; + + qreal scale() const; + + QRect geometry() const; + + int refreshRate() const; + + int depth() const; + QImage::Format format() const; + + PowerState powerState() const; + void setPowerState(PowerState powerState); + + Subpixel subpixel() const; + + Transform transform() const; + + ContentType contentType() const; + void setContentType(ContentType contentType); + +signals: + void manufacturerChanged(const QString &manufacturer); + void modelChanged(const QString &model); + void physicalSizeChanged(const QSize &physicalSize); + void enabledChanged(bool enabled); + void globalPositionChanged(const QPoint &globalPosition); + void modeSizeChanged(const QSize &modeSize); + void pixelSizeChanged(const QSize &pixelSize); + void scaleChanged(qreal scale); + void geometryChanged(const QRect &geometry); + void refreshRateChanged(int refreshRate); + void powerStateChanged(Output::PowerState powerState); + void subpixelChanged(Output::Subpixel subpixel); + void transformChanged(Output::Transform transform); + void contentTypeChanged(Output::ContentType contentType); + void modeAdded(const Mode &mode); + void modeChanged(const Mode &mode); + +protected: + QScopedPointer const d_ptr; +}; + +LIRIAURORACORE_EXPORT QDebug operator<<(QDebug debug, const Output *output); + +typedef QList Outputs; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Output::Capabilities) +Q_DECLARE_OPERATORS_FOR_FLAGS(Output::Mode::Flags) + +} // namespace Core + +} // namespace Aurora + +Q_DECLARE_METATYPE(Aurora::Core::Output::PowerState) +Q_DECLARE_METATYPE(Aurora::Core::Output::Subpixel) +Q_DECLARE_METATYPE(Aurora::Core::Output::Transform) +Q_DECLARE_METATYPE(Aurora::Core::Output::ContentType) +Q_DECLARE_METATYPE(Aurora::Core::Output::Mode) diff --git a/src/core/output_p.h b/src/core/output_p.h new file mode 100644 index 00000000..4f9832a3 --- /dev/null +++ b/src/core/output_p.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +// +// 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. +// + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT OutputPrivate +{ + Q_DECLARE_PUBLIC(Output) +public: + explicit OutputPrivate(Output *self); + + static OutputPrivate *get(Output *output) { return output->d_func(); } + + void setManufacturer(const QString &manufacturer); + void setModel(const QString &model); + void setSubpixel(Output::Subpixel subpixel); + void setTransform(Output::Transform transform); + void setPhysicalSize(const QSize &physicalSize); + void setGlobalPosition(const QPoint &globalPosition); + void setScale(qreal scale); + + QUuid uuid; + QString name; + QString description; + QString manufacturer; + QString model; + QString serialNumber; + QSize physicalSize; + bool enabled = true; + QPoint globalPosition; + qreal scale = 1.0f; + int depth = 0; + QImage::Format format = QImage::Format_Invalid; + Output::PowerState powerState = Output::PowerState::On; + Output::Subpixel subpixel = Output::Subpixel::Unknown; + Output::Transform transform = Output::Transform::Normal; + Output::ContentType contentType = Output::ContentType::Unknown; + QList modes; + QList::iterator currentMode = modes.end(); + +protected: + Output *q_ptr = nullptr; +}; + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/session.cpp b/src/core/session.cpp new file mode 100644 index 00000000..a1f0207c --- /dev/null +++ b/src/core/session.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-FileCopyrightText: 2020 Vlad Zahorodnii +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "session.h" +#include "session_noop_p.h" + +namespace Aurora { + +namespace Core { + +/*! + \class Session + \inmodule AuroraCore + \brief The Session class represents the session controlled by the compositor. + + The Session class provides information about the virtual terminal where the compositor + is running and a way to open files that require special privileges, e.g. DRM devices or + input devices. + */ + +/*! + \enum Session::Type + + This enum type is used to specify the type of the session. + */ + +/*! + \enum Session::Capability + + This enum type is used to specify optional capabilities of the session. + */ + +/*! + \fn Capabilities Session::capabilities() + + Returns the capabilities supported by the session. + */ + +/*! + \fn bool Session::isActive() + + Returns \c true if the session is active; otherwise returns \c false. + */ + +/*! + \fn QString Session::seat() + + Returns the seat name for the Session. + */ + +/*! + \fn uint Session::terminal() + + Returns the terminal controlled by the Session. + */ + +/*! + \fn int Session::openRestricted(const QString &fileName) + + Opens the file with the specified \a fileName. Returns the file descriptor + of the file or \c -1 if an error has occurred. + */ + +/*! + \fn void Session::closeRestricted(int fileDescriptor) + + Closes a file that has been opened using the openRestricted() function. + */ + +/*! + \fn void switchTo(uint terminal) + + Switches to the specified virtual \a terminal. This function does nothing if the + Capability::SwitchTerminal capability is unsupported. + */ + +/*! + \fn void Session::awoke() + + This signal is emitted when the session is resuming from suspend. + */ + +/*! + \fn void Session::activeChanged(bool active) + + This signal is emitted when the active state of the session has changed. + */ + +/*! + \fn void Session::deviceResumed(dev_t deviceId) + + This signal is emitted when the specified device can be used again. + */ + +/*! + \fn void Session::devicePaused(dev_t deviceId) + + This signal is emitted when the given device cannot be used by the compositor + anymore. For example, this normally occurs when switching between VTs. + + Note that when this signal is emitted for a DRM device, master permissions can + be already revoked. + */ + +static const struct +{ + Session::Type type; + std::function()> createFunc; +} s_availableSessions[] = { + { Session::Type::Noop, &NoopSession::create }, +}; + +std::unique_ptr Session::create() +{ + for (const auto &sessionInfo : s_availableSessions) { + std::unique_ptr session = sessionInfo.createFunc(); + if (session) + return session; + } + + return nullptr; +} + +} // namespace Core + +} // namespace Aurora \ No newline at end of file diff --git a/src/core/session.h b/src/core/session.h new file mode 100644 index 00000000..408df84d --- /dev/null +++ b/src/core/session.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-FileCopyrightText: 2020 Vlad Zahorodnii +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT Session : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) +public: + Q_DISABLE_COPY_MOVE(Session) + + enum class Type { + Noop, + }; + Q_ENUM(Type) + + enum class Capability : uint { + SwitchTerminal = 0x1, + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + virtual Capabilities capabilities() const = 0; + + virtual bool isActive() const = 0; + + virtual QString seat() const = 0; + virtual uint terminal() const = 0; + + virtual int openRestricted(const QString &fileName) = 0; + virtual void closeRestricted(int fileDescriptor) = 0; + + virtual void switchTo(uint terminal) = 0; + + static std::unique_ptr create(); + +signals: + void activeChanged(bool active); + void awoke(); + void deviceResumed(dev_t deviceId); + void devicePaused(dev_t deviceId); + +protected: + explicit Session() = default; +}; + +} // namespace Core + +} // namespace Aurora + +Q_DECLARE_OPERATORS_FOR_FLAGS(Aurora::Core::Session::Capabilities) diff --git a/src/core/session_noop.cpp b/src/core/session_noop.cpp new file mode 100644 index 00000000..7590c689 --- /dev/null +++ b/src/core/session_noop.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-FileCopyrightText: 2021 Vlad Zahorodnii +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "session_noop_p.h" + +namespace Aurora { + +namespace Core { + +std::unique_ptr NoopSession::create() +{ + return std::unique_ptr{ new NoopSession() }; +} + +NoopSession::~NoopSession() { } + +NoopSession::Capabilities NoopSession::capabilities() const +{ + return Capabilities(); +} + +bool NoopSession::isActive() const +{ + return true; +} + +QString NoopSession::seat() const +{ + return QStringLiteral("seat0"); +} + +uint NoopSession::terminal() const +{ + return 0; +} + +int NoopSession::openRestricted(const QString &fileName) +{ + return -1; +} + +void NoopSession::closeRestricted(int fileDescriptor) { } + +void NoopSession::switchTo(uint terminal) { } + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/session_noop_p.h b/src/core/session_noop_p.h new file mode 100644 index 00000000..0434d82d --- /dev/null +++ b/src/core/session_noop_p.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-FileCopyrightText: 2021 Vlad Zahorodnii +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// +// 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. +// + +#include "session.h" + +namespace Aurora { + +namespace Core { + +class NoopSession : public Session +{ + Q_OBJECT +public: + static std::unique_ptr create(); + ~NoopSession() override; + + Capabilities capabilities() const override; + + bool isActive() const override; + + QString seat() const override; + uint terminal() const override; + + int openRestricted(const QString &fileName) override; + void closeRestricted(int fileDescriptor) override; + + void switchTo(uint terminal) override; + +private: + explicit NoopSession() = default; +}; + +} // namespace Core + +} // namespace Aurora \ No newline at end of file diff --git a/src/core/window.cpp b/src/core/window.cpp new file mode 100644 index 00000000..f470ce60 --- /dev/null +++ b/src/core/window.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "window.h" + +namespace Aurora { + +namespace Core { + +Window::Window(QObject *parent) : QObject(parent) { } + +Window::~Window() { } + +void *Window::resource(const QByteArray &name) +{ + Q_UNUSED(name) + return nullptr; +} + +} // namespace Core + +} // namespace Aurora diff --git a/src/core/window.h b/src/core/window.h new file mode 100644 index 00000000..699829ca --- /dev/null +++ b/src/core/window.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include + +namespace Aurora { + +namespace Core { + +class LIRIAURORACORE_EXPORT Window : public QObject +{ + Q_OBJECT +public: + ~Window(); + + virtual void *resource(const QByteArray &name); + + virtual bool create() = 0; + virtual void destroy() = 0; + +protected: + explicit Window(QObject *parent = nullptr); +}; + +} // namespace Core + +} // namespace Aurora