From 63280d4bd23c3140ade43046ae5ceacfaa08c6c4 Mon Sep 17 00:00:00 2001 From: Pier Luigi Fiorini Date: Wed, 17 Jan 2024 20:55:46 +0100 Subject: [PATCH] Say hello to the new aurora-eglfs New Qt platform plugin for Aurora Wayland compositors. This is an initial an non-functional version to test the Aurora platform abstraction library (AuroraCore). Closes: #46 --- CMakeLists.txt | 1 + features.cmake | 10 +- src/plugins/platforms/eglfs/CMakeLists.txt | 52 +++ src/plugins/platforms/eglfs/aurora-eglfs.json | 3 + .../platforms/eglfs/eglfsconfigchooser.cpp | 9 + .../platforms/eglfs/eglfsconfigchooser.h | 12 + src/plugins/platforms/eglfs/eglfscontext.cpp | 53 +++ src/plugins/platforms/eglfs/eglfscontext.h | 23 ++ src/plugins/platforms/eglfs/eglfscursor.cpp | 27 ++ src/plugins/platforms/eglfs/eglfscursor.h | 26 ++ src/plugins/platforms/eglfs/eglfsinfo.cpp | 109 ++++++ src/plugins/platforms/eglfs/eglfsinfo.h | 16 + .../platforms/eglfs/eglfsinputmanager.cpp | 120 ++++++ .../platforms/eglfs/eglfsinputmanager.h | 26 ++ .../platforms/eglfs/eglfsintegration.cpp | 360 ++++++++++++++++++ .../platforms/eglfs/eglfsintegration.h | 91 +++++ src/plugins/platforms/eglfs/eglfsmain.cpp | 40 ++ src/plugins/platforms/eglfs/eglfsscreen.cpp | 137 +++++++ src/plugins/platforms/eglfs/eglfsscreen.h | 57 +++ src/plugins/platforms/eglfs/eglfswindow.cpp | 207 ++++++++++ src/plugins/platforms/eglfs/eglfswindow.h | 82 ++++ 21 files changed, 1456 insertions(+), 5 deletions(-) create mode 100644 src/plugins/platforms/eglfs/CMakeLists.txt create mode 100644 src/plugins/platforms/eglfs/aurora-eglfs.json create mode 100644 src/plugins/platforms/eglfs/eglfsconfigchooser.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsconfigchooser.h create mode 100644 src/plugins/platforms/eglfs/eglfscontext.cpp create mode 100644 src/plugins/platforms/eglfs/eglfscontext.h create mode 100644 src/plugins/platforms/eglfs/eglfscursor.cpp create mode 100644 src/plugins/platforms/eglfs/eglfscursor.h create mode 100644 src/plugins/platforms/eglfs/eglfsinfo.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsinfo.h create mode 100644 src/plugins/platforms/eglfs/eglfsinputmanager.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsinputmanager.h create mode 100644 src/plugins/platforms/eglfs/eglfsintegration.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsintegration.h create mode 100644 src/plugins/platforms/eglfs/eglfsmain.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsscreen.cpp create mode 100644 src/plugins/platforms/eglfs/eglfsscreen.h create mode 100644 src/plugins/platforms/eglfs/eglfswindow.cpp create mode 100644 src/plugins/platforms/eglfs/eglfswindow.h diff --git a/CMakeLists.txt b/CMakeLists.txt index edb5a023..a3a07da9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ if(FEATURE_aurora_compositor_quick) endif() endif() if(FEATURE_aurora_qpa) + add_subdirectory(src/plugins/platforms/eglfs) # add_subdirectory(src/platformsupport/libinput) endif() if(FEATURE_aurora_deviceintegration_wayland) diff --git a/features.cmake b/features.cmake index 549334ab..d6ea0d30 100644 --- a/features.cmake +++ b/features.cmake @@ -314,23 +314,23 @@ if(FEATURE_aurora_qpa) if(NOT TARGET EGL::EGL) message(WARNING "You need EGL for Aurora::QPA") - set(FEATURE_aurora_qpa OFF) + set(FEATURE_aurora_qpa OFF) endif() if(NOT TARGET PkgConfig::Libudev) message(WARNING "You need udev for Aurora::QPA") - set(FEATURE_aurora_qpa OFF) + set(FEATURE_aurora_qpa OFF) endif() if(NOT TARGET PkgConfig::Libinput) message(WARNING "You need libinput for Aurora::QPA") - set(FEATURE_aurora_qpa OFF) + set(FEATURE_aurora_qpa OFF) endif() if(NOT TARGET PkgConfig::Libdrm) message(WARNING "You need libdrm for Aurora::QPA") - set(FEATURE_aurora_qpa OFF) + set(FEATURE_aurora_qpa OFF) endif() if(NOT TARGET PkgConfig::Gbm) message(WARNING "You need gbm for Aurora::QPA") - set(FEATURE_aurora_qpa OFF) + set(FEATURE_aurora_qpa OFF) endif() if(NOT FEATURE_aurora_xkbcommon) message(WARNING "You need XkbCommon support for Aurora::QPA") diff --git a/src/plugins/platforms/eglfs/CMakeLists.txt b/src/plugins/platforms/eglfs/CMakeLists.txt new file mode 100644 index 00000000..78b6e6ac --- /dev/null +++ b/src/plugins/platforms/eglfs/CMakeLists.txt @@ -0,0 +1,52 @@ +include(ECMQtDeclareLoggingCategory) +ecm_qt_declare_logging_category( + AuroraEglFS_SOURCES + HEADER "eglfscategories.h" + IDENTIFIER "gLcEglFS" + CATEGORY_NAME "aurora.eglfs" + DEFAULT_SEVERITY "Info" + DESCRIPTION "Aurora EGLFS Qt platform plugin" +) + +qt6_add_plugin(AuroraEglFSPlatformPlugin + SHARED + CLASS_NAME EglFSIntegrationPlugin + MANUAL_FINALIZATION + eglfsconfigchooser.cpp eglfsconfigchooser.h + eglfscontext.cpp eglfscontext.h + eglfscursor.cpp eglfscursor.h + eglfsinfo.cpp eglfsinfo.h + eglfsinputmanager.cpp eglfsinputmanager.h + eglfsintegration.cpp eglfsintegration.h + eglfsmain.cpp + eglfsscreen.cpp eglfsscreen.h + eglfswindow.cpp eglfswindow.h + aurora-eglfs.json + ${AuroraEglFS_SOURCES} +) + +set_target_properties(AuroraEglFSPlatformPlugin + PROPERTIES OUTPUT_NAME aurora-eglfs +) + +target_compile_definitions(AuroraEglFSPlatformPlugin PRIVATE QT_EGL_NO_X11) + +target_link_libraries(AuroraEglFSPlatformPlugin + PRIVATE + Qt6::Core + Qt6::CorePrivate + Qt6::DBus + Qt6::Gui + Qt6::GuiPrivate + Fontconfig::Fontconfig + EGL::EGL + Liri::AuroraPlatform + Liri::AuroraPlatformPrivate +) + +qt6_finalize_target(AuroraEglFSPlatformPlugin) + +install( + TARGETS AuroraEglFSPlatformPlugin + DESTINATION ${KDE_INSTALL_PLUGINDIR}/platforms +) \ No newline at end of file diff --git a/src/plugins/platforms/eglfs/aurora-eglfs.json b/src/plugins/platforms/eglfs/aurora-eglfs.json new file mode 100644 index 00000000..04cb6e21 --- /dev/null +++ b/src/plugins/platforms/eglfs/aurora-eglfs.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "aurora-eglfs" ] +} diff --git a/src/plugins/platforms/eglfs/eglfsconfigchooser.cpp b/src/plugins/platforms/eglfs/eglfsconfigchooser.cpp new file mode 100644 index 00000000..7e0eb67c --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsconfigchooser.cpp @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "eglfsconfigchooser.h" + +EglFSConfigChooser::EglFSConfigChooser(EGLDisplay display) + : QEglConfigChooser(display) +{ +} diff --git a/src/plugins/platforms/eglfs/eglfsconfigchooser.h b/src/plugins/platforms/eglfs/eglfsconfigchooser.h new file mode 100644 index 00000000..6adb8f84 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsconfigchooser.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +class EglFSConfigChooser : public QEglConfigChooser +{ +public: + EglFSConfigChooser(EGLDisplay display); +}; diff --git a/src/plugins/platforms/eglfs/eglfscontext.cpp b/src/plugins/platforms/eglfs/eglfscontext.cpp new file mode 100644 index 00000000..f48c0556 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfscontext.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include +#include + +#include + +#include "eglfscategories.h" +#include "eglfscontext.h" +#include "eglfswindow.h" + +EglFSContext::EglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, + EGLDisplay display, EGLConfig *config) + : QEGLPlatformContext(format, share, display, config, + Aurora::Platform::auroraDeviceIntegration()->supportsSurfacelessContexts() + ? Flags() + : QEGLPlatformContext::NoSurfaceless) +{ +} + +void EglFSContext::swapBuffers(QPlatformSurface *surface) +{ + auto *platformWindow = static_cast(surface); + if (platformWindow) { + Aurora::Platform::auroraDeviceIntegration()->waitForVSync(platformWindow->auroraWindow()); + QEGLPlatformContext::swapBuffers(surface); + Aurora::Platform::auroraDeviceIntegration()->presentBuffer(platformWindow->auroraWindow()); + } +} + +EGLSurface EglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) +{ + if (surface->surface()->surfaceClass() == QSurface::Window) + return static_cast(surface)->surface(); + else + return static_cast(surface)->pbuffer(); +} + +void EglFSContext::runGLChecks() +{ + // Beware that QOpenGLContext and QOpenGLFunctions are not yet usable at this stage + + const char *renderer = reinterpret_cast(glGetString(GL_RENDERER)); + if (renderer) { + // Warn about unsupported or limited hardware + if (strstr(renderer, "llvmpipe")) + qCWarning(gLcEglFS, + "Running on a software rasterizer (LLVMpipe): performance will be limited"); + } +} diff --git a/src/plugins/platforms/eglfs/eglfscontext.h b/src/plugins/platforms/eglfs/eglfscontext.h new file mode 100644 index 00000000..ada2da0a --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfscontext.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include + +class EglFSContext : public QEGLPlatformContext +{ +public: + using QEGLPlatformContext::QEGLPlatformContext; + EglFSContext() = default; + EglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, + EGLConfig *config); + + void swapBuffers(QPlatformSurface *surface) override; + +protected: + EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override; + void runGLChecks() override; +}; diff --git a/src/plugins/platforms/eglfs/eglfscursor.cpp b/src/plugins/platforms/eglfs/eglfscursor.cpp new file mode 100644 index 00000000..5da53b98 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfscursor.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include "eglfscursor.h" +#include "eglfsscreen.h" + +EglFSCursor::EglFSCursor(QPlatformScreen *screen) + : QPlatformCursor() + , m_screen(static_cast(screen)) +{ +} + +EglFSCursor::~EglFSCursor() +{ +} + +#ifndef QT_NO_CURSOR +void EglFSCursor::changeCursor(QCursor *cursor, QWindow *qtWindow) +{ + auto *window = Aurora::Platform::auroraDeviceIntegration()->getWindow(qtWindow); + if (window) + window->changeCursor(cursor); +} +#endif diff --git a/src/plugins/platforms/eglfs/eglfscursor.h b/src/plugins/platforms/eglfs/eglfscursor.h new file mode 100644 index 00000000..a7d9b068 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfscursor.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include +#include + +class EglFSScreen; + +class EglFSCursor : public QPlatformCursor +{ + Q_OBJECT +public: + explicit EglFSCursor(QPlatformScreen *screen); + ~EglFSCursor(); + +#ifndef QT_NO_CURSOR + void changeCursor(QCursor *cursor, QWindow *widget) override; +#endif + +private: + QPointer m_screen; +}; diff --git a/src/plugins/platforms/eglfs/eglfsinfo.cpp b/src/plugins/platforms/eglfs/eglfsinfo.cpp new file mode 100644 index 00000000..eba8c5ea --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsinfo.cpp @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "eglfscategories.h" +#include "eglfsinfo.h" + +#include +#include + +const char *tab = " "; + +static void logGLextensions(const char *prefix, const QList &extensions) +{ + QString extensionString; + const int maxLineLength = 98; + int currentLineLength = 0; + + for (int i = 0; i < extensions.size(); i++) { + const auto extension = extensions[i]; + + // Check if adding the current extension fits in the current line + if (currentLineLength + extension.length() + 2 <= maxLineLength) { // 2 accounts for " " + if (!extensionString.isEmpty()) { + extensionString += QStringLiteral(" "); + currentLineLength += 2; + } + extensionString += QString::fromUtf8(extension); + currentLineLength += extension.length(); + } else { + extensionString += QStringLiteral("\n") + QString::fromUtf8(extension); + currentLineLength = extension.length(); + } + } + + if (!extensionString.isEmpty()) { + const auto numSpaces = qMax(0, 18 - strlen(prefix) - 1); + auto lines = extensionString.split(QLatin1Char('\n')); + + for (int i = 0; i < lines.size(); i++) { + const auto line = lines[i]; + if (i == 0) + qCInfo(gLcEglFS, "%s:%*s%s", prefix, numSpaces, " ", qPrintable(line)); + else + qCInfo(gLcEglFS, "%s%s", tab, qPrintable(line)); + } + } +} + +void logGLInfo() +{ + const char *str; + + str = reinterpret_cast(glGetString(GL_VERSION)); + qCInfo(gLcEglFS, "GL version: %s", str ? str : "(null)"); + + str = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)); + qCInfo(gLcEglFS, "GLSL version: %s", str ? str : "(null)"); + + str = reinterpret_cast(glGetString(GL_VENDOR)); + qCInfo(gLcEglFS, "GL vendor: %s", str ? str : "(null)"); + + str = reinterpret_cast(glGetString(GL_RENDERER)); + qCInfo(gLcEglFS, "GL renderer: %s", str ? str : "(null)"); + + QList extensions = + QByteArray(reinterpret_cast(glGetString(GL_EXTENSIONS))).split(' '); + logGLextensions("GL extensions", extensions); +} + +void logEGLInfo(EGLDisplay display) +{ + const char *str; + + str = eglQueryString(display, EGL_VERSION); + qCInfo(gLcEglFS, "EGL version: %s", str ? str : "(null)"); + + str = eglQueryString(display, EGL_VENDOR); + qCInfo(gLcEglFS, "EGL vendor: %s", str ? str : "(null)"); + + str = eglQueryString(display, EGL_CLIENT_APIS); + qCInfo(gLcEglFS, "EGL client APIs: %s", str ? str : "(null)"); + + QList extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' '); + logGLextensions("EGL extensions", extensions); +} + +void logEGLConfigInfo(EGLDisplay display, EGLConfig config) +{ + if (!config) + return; + + EGLint r, g, b, a; + + qCInfo(gLcEglFS, "EGL attributes:"); + + if (eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r) + && eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g) + && eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b) + && eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a)) + qCInfo(gLcEglFS, "%sRGBA bits: %d %d %d %d", tab, r, g, b, a); + else + qCInfo(gLcEglFS, "%sRGBA bits: unknown", tab); + + if (eglGetConfigAttrib(display, config, EGL_MIN_SWAP_INTERVAL, &a) + && eglGetConfigAttrib(display, config, EGL_MAX_SWAP_INTERVAL, &b)) + qCInfo(gLcEglFS, "%sSwap interval range: %d - %d", tab, a, b); + else + qCInfo(gLcEglFS, "%sSwap interval range: unknown", tab); +} diff --git a/src/plugins/platforms/eglfs/eglfsinfo.h b/src/plugins/platforms/eglfs/eglfsinfo.h new file mode 100644 index 00000000..da37e621 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsinfo.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include + +extern "C" { + +void logGLInfo(); +void logEGLInfo(EGLDisplay display); +void logEGLConfigInfo(EGLDisplay display, EGLConfig config); +} diff --git a/src/plugins/platforms/eglfs/eglfsinputmanager.cpp b/src/plugins/platforms/eglfs/eglfsinputmanager.cpp new file mode 100644 index 00000000..ac6d4068 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsinputmanager.cpp @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include + +#include + +#include +#include +#include +#include + +#include "eglfsinputmanager.h" + +EglFSInputManager::EglFSInputManager(QObject *parent) + : QObject(parent) + , m_inputManager(Aurora::Platform::auroraDeviceIntegration()->createInputManager(this)) +{ + const auto keyboards = m_inputManager->keyboardDevices(); + for (auto *keyboard : keyboards) + handleKeyboardAdded(keyboard); + + const auto pointers = m_inputManager->pointerDevices(); + for (auto *pointer : pointers) + handlePointerAdded(pointer); + + const auto touchs = m_inputManager->touchDevices(); + for (auto *touch : touchs) + handleTouchAdded(touch); + + connect(m_inputManager, &Aurora::Platform::InputManager::keyboardAdded, this, + &EglFSInputManager::handleKeyboardAdded); + connect(m_inputManager, &Aurora::Platform::InputManager::pointerAdded, this, + &EglFSInputManager::handlePointerAdded); + connect(m_inputManager, &Aurora::Platform::InputManager::touchAdded, this, + &EglFSInputManager::handleTouchAdded); +} + +void EglFSInputManager::handleInputDeviceAddedOrRemoved() +{ + QInputDeviceManager *inputManager = QGuiApplicationPrivate::inputDeviceManager(); + QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get(inputManager); + + inputManagerPriv->setDeviceCount( + QInputDeviceManager::DeviceTypePointer, + m_inputManager->deviceCount(Aurora::Platform::InputDevice::DeviceType::Pointer)); + inputManagerPriv->setDeviceCount( + QInputDeviceManager::DeviceTypeKeyboard, + m_inputManager->deviceCount(Aurora::Platform::InputDevice::DeviceType::Keyboard)); + inputManagerPriv->setDeviceCount( + QInputDeviceManager::DeviceTypeTouch, + m_inputManager->deviceCount(Aurora::Platform::InputDevice::DeviceType::Touch)); + inputManagerPriv->setDeviceCount( + QInputDeviceManager::DeviceTypeTablet, + m_inputManager->deviceCount(Aurora::Platform::InputDevice::DeviceType::Tablet)); +} + +void EglFSInputManager::handleKeyboardAdded(Aurora::Platform::KeyboardDevice *keyboard) +{ + connect(keyboard, &Aurora::Platform::KeyboardDevice::keyPressed, this, + [](const Aurora::Platform::KeyboardDevice::KeyEvent &keyEvent) { + QWindowSystemInterface::handleExtendedKeyEvent( + nullptr, keyEvent.timestamp, QEvent::KeyPress, keyEvent.key, + keyEvent.modifiers, keyEvent.nativeScanCode, keyEvent.nativeVirtualKey, + keyEvent.nativeModifiers, keyEvent.text, keyEvent.autoRepeat, + keyEvent.repeatCount); + }); + connect(keyboard, &Aurora::Platform::KeyboardDevice::keyReleased, this, + [](const Aurora::Platform::KeyboardDevice::KeyEvent &keyEvent) { + QWindowSystemInterface::handleExtendedKeyEvent( + nullptr, keyEvent.timestamp, QEvent::KeyRelease, keyEvent.key, + keyEvent.modifiers, keyEvent.nativeScanCode, keyEvent.nativeVirtualKey, + keyEvent.nativeModifiers, keyEvent.text, keyEvent.autoRepeat, + keyEvent.repeatCount); + }); +} + +void EglFSInputManager::handlePointerAdded(Aurora::Platform::PointerDevice *pointer) +{ + connect(pointer, &Aurora::Platform::PointerDevice::motion, this, + [this](const QPointF &absPosition) { + Q_UNUSED(time) + + m_pos = absPosition; + + Qt::KeyboardModifiers mods = + QGuiApplicationPrivate::inputDeviceManager()->keyboardModifiers(); + QWindowSystemInterface::handleMouseEvent(nullptr, absPosition, absPosition, + m_buttons, Qt::NoButton, QEvent::MouseMove, + mods); + }); + connect(pointer, &Aurora::Platform::PointerDevice::buttonPressed, this, + [this](Qt::MouseButton button) { + Q_UNUSED(time) + + m_buttons.setFlag(button, true); + + Qt::KeyboardModifiers mods = + QGuiApplicationPrivate::inputDeviceManager()->keyboardModifiers(); + QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons, button, + QEvent::MouseButtonPress, mods); + }); + connect(pointer, &Aurora::Platform::PointerDevice::buttonReleased, this, + [this](Qt::MouseButton button) { + Q_UNUSED(time) + + m_buttons.setFlag(button, false); + + Qt::KeyboardModifiers mods = + QGuiApplicationPrivate::inputDeviceManager()->keyboardModifiers(); + QWindowSystemInterface::handleMouseEvent(nullptr, m_pos, m_pos, m_buttons, button, + QEvent::MouseButtonRelease, mods); + }); +} + +void EglFSInputManager::handleTouchAdded(Aurora::Platform::TouchDevice *touch) +{ + Q_UNUSED(touch); +} diff --git a/src/plugins/platforms/eglfs/eglfsinputmanager.h b/src/plugins/platforms/eglfs/eglfsinputmanager.h new file mode 100644 index 00000000..a31d7262 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsinputmanager.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include + +class EglFSInputManager : public QObject +{ + Q_OBJECT +public: + explicit EglFSInputManager(QObject *parent = nullptr); + +private: + Aurora::Platform::InputManager *m_inputManager = nullptr; + Qt::MouseButtons m_buttons = Qt::NoButton; + QPointF m_pos; + +private slots: + void handleInputDeviceAddedOrRemoved(); + void handleKeyboardAdded(Aurora::Platform::KeyboardDevice *keyboard); + void handlePointerAdded(Aurora::Platform::PointerDevice *pointer); + void handleTouchAdded(Aurora::Platform::TouchDevice *touch); +}; diff --git a/src/plugins/platforms/eglfs/eglfsintegration.cpp b/src/plugins/platforms/eglfs/eglfsintegration.cpp new file mode 100644 index 00000000..51b72f18 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsintegration.cpp @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#ifndef QT_NO_OPENGL +# include +# include +#endif + +#include + +#include +#include +#include + +#ifndef QT_NO_OPENGL +# include +#endif +#include +#include +#include +#include + +#include + +#include "eglfscategories.h" +#include "eglfsinfo.h" +#include "eglfsinputmanager.h" +#include "eglfsintegration.h" +#include "eglfsscreen.h" +#include "eglfswindow.h" + +EglFSIntegration::EglFSIntegration(const EglFSIntegrationOptions &options) + : QPlatformIntegration() + , QPlatformNativeInterface() + , m_options(options) + , m_loop(new QEventLoop(this)) + , m_fontDb(new QGenericUnixFontDatabase()) + , m_services(new QGenericUnixServices()) +{ + auto *integration = Aurora::Platform::auroraDeviceIntegration(); + connect(integration, &Aurora::Platform::DeviceIntegration::statusChanged, this, + &EglFSIntegration::handleIntegrationStatusChanged, Qt::QueuedConnection); + connect(integration, &Aurora::Platform::DeviceIntegration::outputAdded, this, + &EglFSIntegration::handleOutputAdded); + connect(integration, &Aurora::Platform::DeviceIntegration::outputRemoved, this, + &EglFSIntegration::handleOutputRemoved); +} + +EglFSIntegration::~EglFSIntegration() +{ +} + +void EglFSIntegration::initialize() +{ + Aurora::Platform::auroraDeviceIntegration()->initialize(); + + // Wait until the integration is ready + m_loop->exec(); + if (!m_ready) { + destroy(); + qCFatal(gLcEglFS, "Aborting..."); + } + + // Create EGL display + // auto nativeDisplay = Aurora::Platform::auroraDeviceIntegration()->platformDisplay(); + m_display = Aurora::Platform::auroraDeviceIntegration()->eglDisplay(); + if (Q_UNLIKELY(m_display == EGL_NO_DISPLAY)) + qCFatal(gLcEglFS, "Failed to open EGL display, cannot continue"); + + // Initialize EGL + EGLint major, minor; + if (Q_UNLIKELY(!eglInitialize(m_display, &major, &minor))) + qCFatal(gLcEglFS, "Could not initialize EGL display, cannot continue"); + + m_inputContext = QPlatformInputContextFactory::create(); + + if (!m_options.disableInputHandlers) + m_inputManager.reset(new EglFSInputManager()); +} + +void EglFSIntegration::destroy() +{ + qCDebug(gLcEglFS) << "Device integration is about to be destroyed..."; + + Aurora::Platform::auroraDeviceIntegration()->destroy(); + + m_display = EGL_NO_DISPLAY; +} + +EGLDisplay EglFSIntegration::display() const +{ + return m_display; +} + +QAbstractEventDispatcher *EglFSIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + +QPlatformFontDatabase *EglFSIntegration::fontDatabase() const +{ + return m_fontDb.data(); +} + +QPlatformServices *EglFSIntegration::services() const +{ + return m_services.data(); +} + +QPlatformInputContext *EglFSIntegration::inputContext() const +{ + return m_inputContext; +} + +QPlatformTheme *EglFSIntegration::createPlatformTheme(const QString &name) const +{ + return QGenericUnixTheme::createUnixTheme(name); +} + +QPlatformWindow *EglFSIntegration::createPlatformWindow(QWindow *window) const +{ + QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + + // Only toplevel windows with OpenGL surfaces are allowed since this platform plugin is + // tailored for Aurora Wayland compositors that all use QtQuick + if (window->transientParent()) { + qCWarning(gLcEglFS) << "Window" << window + << "has a transient parent: only toplevel windows are allowed"; + return nullptr; + } + if (window->surfaceType() == QWindow::SurfaceType::RasterSurface + || window->surfaceType() == QWindow::SurfaceType::RasterGLSurface) { + qCWarning(gLcEglFS) << "Window" << window << "has a raster surface which is not allowed"; + return nullptr; + } + + // Only a window for screen is allowed + const auto topLevelWindows = QGuiApplication::topLevelWindows(); + for (auto *topLevelWindow : topLevelWindows) { + if (topLevelWindow != window && topLevelWindow->screen() == window->screen()) { + qCWarning(gLcEglFS) << "Window" << window + << "cannot be created: there is already a window on the same screen" + << window->screen()->name(); + return nullptr; + } + } + + // Create the platform window + auto *platformWindow = new EglFSWindow(const_cast(this), window); + if (!platformWindow->create()) { + delete platformWindow; + return nullptr; + } + + // Show without activating + const auto showWithoutActivating = window->property("_q_showWithoutActivating"); + if (showWithoutActivating.isValid() && showWithoutActivating.toBool()) + return platformWindow; + + // Activate only the window for the primary screen to make input work + if (window->type() != Qt::ToolTip && window->screen() == QGuiApplication::primaryScreen()) + platformWindow->requestActivateWindow(); + + return platformWindow; +} + +QPlatformBackingStore *EglFSIntegration::createPlatformBackingStore(QWindow *window) const +{ + Q_UNUSED(window); + return nullptr; +} + +#ifndef QT_NO_OPENGL +QPlatformOpenGLContext *EglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + QPlatformOpenGLContext *share = context->shareHandle(); + + QSurfaceFormat adjustedFormat = + Aurora::Platform::auroraDeviceIntegration()->surfaceFormatFor(context->format()); + + EGLConfig config = + Aurora::Platform::auroraDeviceIntegration()->chooseConfig(m_display, adjustedFormat); + EglFSContext *ctx = new EglFSContext(adjustedFormat, share, m_display, &config); + + // Print some information + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->eglContext()); + logGLInfo(); + logEGLInfo(m_display); + if (config) + logEGLConfigInfo(m_display, config); + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + return ctx; +} + +QOpenGLContext *EglFSIntegration::createOpenGLContext(EGLContext context, EGLDisplay contextDisplay, + QOpenGLContext *shareContext) const +{ + return QEGLPlatformContext::createFrom(context, contextDisplay, display(), + shareContext); +} + +QPlatformOffscreenSurface * +EglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + Q_UNUSED(surface); + return nullptr; +} +#endif + +#if QT_CONFIG(vulkan) +QPlatformVulkanInstance * +EglFSIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const +{ + Q_UNUSED(instance); + return nullptr; +} +#endif + +bool EglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: + return true; +#ifndef QT_NO_OPENGL + case OpenGL: + return true; + case ThreadedOpenGL: + return true; + case RasterGLSurface: + return false; +#else + case OpenGL: + return false; + case ThreadedOpenGL: + return false; + case RasterGLSurface: + return false; +#endif + case WindowMasks: + return false; + case MultipleWindows: + return true; + case ForeignWindows: + return false; + case WindowManagement: + return false; + case WindowActivation: + return true; + case MaximizeUsingFullscreenGeometry: + return true; + case NonFullScreenWindows: + return false; + case OpenGLOnRasterSurface: + return false; + case ApplicationState: + return false; + default: + return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformNativeInterface *EglFSIntegration::nativeInterface() const +{ + return const_cast(this); +} + +void *EglFSIntegration::nativeResourceForIntegration(const QByteArray &resource) +{ + Q_UNUSED(resource); + return nullptr; +} + +void *EglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen) +{ + Q_UNUSED(resource); + Q_UNUSED(screen); + return nullptr; +} + +void *EglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + Q_UNUSED(resource); + Q_UNUSED(window); + return nullptr; +} + +#ifndef QT_NO_OPENGL +void *EglFSIntegration::nativeResourceForContext(const QByteArray &resource, + QOpenGLContext *context) +{ + Q_UNUSED(resource); + Q_UNUSED(context); + return nullptr; +} +#endif + +QPlatformNativeInterface::NativeResourceForContextFunction +EglFSIntegration::nativeResourceFunctionForContext(const QByteArray &resource) +{ + Q_UNUSED(resource); + return NativeResourceForContextFunction(); +} + +QFunctionPointer EglFSIntegration::platformFunction(const QByteArray &function) const +{ + Q_UNUSED(function); + return QFunctionPointer(); +} + +QVariant EglFSIntegration::styleHint(QPlatformIntegration::StyleHint hint) const +{ + return QPlatformIntegration::styleHint(hint); +} + +void EglFSIntegration::handleIntegrationStatusChanged( + Aurora::Platform::DeviceIntegration::Status status) +{ + switch (status) { + case Aurora::Platform::DeviceIntegration::Status::Ready: + qCInfo(gLcEglFS) << "Device integration initialized successfully"; + m_ready = true; + m_loop->quit(); + break; + case Aurora::Platform::DeviceIntegration::Status::Failed: + qCWarning(gLcEglFS) << "Device integration initialization failed"; + m_ready = false; + m_loop->quit(); + break; + default: + break; + } +} + +void EglFSIntegration::handleOutputAdded(Aurora::Platform::Output *output) +{ + const auto isPrimary = QGuiApplication::screens().length() == 0; + auto *platformScreen = new EglFSScreen(output); + output->setScreen(platformScreen->screen()); + QWindowSystemInterface::handleScreenAdded(platformScreen, isPrimary); + qCDebug(gLcEglFS) << "Creating screen" << platformScreen << "(" << platformScreen->name() << ")" + << "with geometry" << platformScreen->geometry() << "and isPrimary" + << isPrimary; +} + +void EglFSIntegration::handleOutputRemoved(Aurora::Platform::Output *output) +{ + const auto screens = QGuiApplication::screens(); + for (auto *screen : qAsConst(screens)) { + auto *platformScreen = static_cast(screen->handle()); + if (platformScreen && platformScreen->auroraOutput() == output) { + QWindowSystemInterface::handleScreenRemoved(platformScreen); + break; + } + } +} diff --git a/src/plugins/platforms/eglfs/eglfsintegration.h b/src/plugins/platforms/eglfs/eglfsintegration.h new file mode 100644 index 00000000..feb3446b --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsintegration.h @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include +#include + +#include +#include + +#include "eglfscontext.h" + +class QEventLoop; + +class EglFSInputManager; + +struct EglFSIntegrationOptions +{ + bool disableInputHandlers = false; +}; + +class EglFSIntegration + : public QPlatformIntegration + , public QPlatformNativeInterface +#ifndef QT_NO_OPENGL + , public QNativeInterface::Private::QEGLIntegration +#endif +{ +public: + EglFSIntegration(const EglFSIntegrationOptions &options); + ~EglFSIntegration(); + + void initialize() override; + void destroy() override; + + EGLDisplay display() const; + + QAbstractEventDispatcher *createEventDispatcher() const override; + QPlatformFontDatabase *fontDatabase() const override; + QPlatformServices *services() const override; + QPlatformInputContext *inputContext() const override; + QPlatformTheme *createPlatformTheme(const QString &name) const override; + + QPlatformWindow *createPlatformWindow(QWindow *window) const override; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; +#ifndef QT_NO_OPENGL + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; + QOpenGLContext *createOpenGLContext(EGLContext context, EGLDisplay display, + QOpenGLContext *shareContext) const override; + QPlatformOffscreenSurface * + createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; +#endif +#if QT_CONFIG(vulkan) + QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override; +#endif + bool hasCapability(QPlatformIntegration::Capability cap) const override; + + QPlatformNativeInterface *nativeInterface() const override; + + // QPlatformNativeInterface + void *nativeResourceForIntegration(const QByteArray &resource) override; + void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override; + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; +#ifndef QT_NO_OPENGL + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override; +#endif + NativeResourceForContextFunction + nativeResourceFunctionForContext(const QByteArray &resource) override; + + QFunctionPointer platformFunction(const QByteArray &function) const override; + + QVariant styleHint(QPlatformIntegration::StyleHint hint) const override; + +private: + EglFSIntegrationOptions m_options; + QEventLoop *m_loop = nullptr; + bool m_ready = false; + EGLDisplay m_display = EGL_NO_DISPLAY; + QPlatformInputContext *m_inputContext = nullptr; + QScopedPointer m_fontDb; + QScopedPointer m_services; + QScopedPointer m_inputManager; + +private slots: + void handleIntegrationStatusChanged(Aurora::Platform::DeviceIntegration::Status status); + void handleOutputAdded(Aurora::Platform::Output *output); + void handleOutputRemoved(Aurora::Platform::Output *output); +}; diff --git a/src/plugins/platforms/eglfs/eglfsmain.cpp b/src/plugins/platforms/eglfs/eglfsmain.cpp new file mode 100644 index 00000000..e7e1cff8 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsmain.cpp @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "eglfsintegration.h" + +class EglFSIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "aurora-eglfs.json") +public: + QPlatformIntegration *create(const QString &, const QStringList &) override; +}; + +QPlatformIntegration *EglFSIntegrationPlugin::create(const QString &system, + const QStringList ¶mList) +{ + if (system.compare(QLatin1String("aurora-eglfs"), Qt::CaseInsensitive) == 0) { + // Set options from parameters passed from the command line like this + // -platform aurora-eglfs-next:disable-input-handlers=true + EglFSIntegrationOptions options; + for (const auto ¶m : qAsConst(paramList)) { + const auto paramArgs = param.split(QLatin1Char('=')); + if (paramArgs.length() == 2) { + const auto name = paramArgs[0].trimmed().toLower(); + const auto value = paramArgs[1].trimmed(); + + if (name == QStringLiteral("disable-input-handlers")) + options.disableInputHandlers = QVariant::fromValue(value).toBool(); + } + } + + return new EglFSIntegration(options); + } + + return nullptr; +} + +#include "eglfsmain.moc" diff --git a/src/plugins/platforms/eglfs/eglfsscreen.cpp b/src/plugins/platforms/eglfs/eglfsscreen.cpp new file mode 100644 index 00000000..5979dd1f --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsscreen.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "eglfscursor.h" +#include "eglfsscreen.h" + +EglFSScreen::EglFSScreen(Aurora::Platform::Output *output) + : QObject() + , QPlatformScreen() + , m_output(output) + , m_cursor(new EglFSCursor(this)) +{ + connect(output, &Aurora::Platform::Output::modeChanged, this, [this]() { + QWindowSystemInterface::handleScreenGeometryChange(screen(), m_output->geometry(), + m_output->geometry()); + QWindowSystemInterface::handleScreenRefreshRateChange( + screen(), static_cast(m_output->refreshRate()) / 1000.0); + }); +} + +EglFSScreen::~EglFSScreen() +{ + if (m_cursor) { + m_cursor->deleteLater(); + m_cursor = nullptr; + } +} + +Aurora::Platform::Output *EglFSScreen::auroraOutput() const +{ + return m_output.data(); +} + +QString EglFSScreen::name() const +{ + return m_output->name(); +} + +QString EglFSScreen::manufacturer() const +{ + return m_output->manufacturer(); +} + +QString EglFSScreen::model() const +{ + return m_output->model(); +} + +QString EglFSScreen::serialNumber() const +{ + return m_output->serialNumber(); +} + +QRect EglFSScreen::geometry() const +{ + return m_output->geometry(); +} + +qreal EglFSScreen::refreshRate() const +{ + return static_cast(m_output->refreshRate()) / 1000.0; +} + +int EglFSScreen::depth() const +{ + return m_output->depth(); +} + +QImage::Format EglFSScreen::format() const +{ + return m_output->format(); +} + +QSizeF EglFSScreen::physicalSize() const +{ + return m_output->physicalSize(); +} + +QDpi EglFSScreen::logicalDpi() const +{ + const auto dpi = 96 * m_output->scale(); + return QDpi(dpi, dpi); +} + +QDpi EglFSScreen::logicalBaseDpi() const +{ + // This is the base logical DPI for the platform, that corresponds + // to a standard-DPI (1x) display. + // + // Qt will use this value together with logicalDpi to compute + // the scale factor as follows: + // factor = logicalDPI / baseDPI + return QDpi(96, 96); +} + +Qt::ScreenOrientation EglFSScreen::nativeOrientation() const +{ + return Qt::PrimaryOrientation; +} + +Qt::ScreenOrientation EglFSScreen::orientation() const +{ + return Qt::PrimaryOrientation; +} + +QPlatformCursor *EglFSScreen::cursor() const +{ + return m_cursor; +} + +QPlatformScreen::SubpixelAntialiasingType EglFSScreen::subpixelAntialiasingTypeHint() const +{ + switch (m_output->subpixel()) { + case Aurora::Platform::Output::Subpixel::HorizontalRGB: + return Subpixel_RGB; + case Aurora::Platform::Output::Subpixel::HorizontalBGR: + return Subpixel_BGR; + case Aurora::Platform::Output::Subpixel::VerticalRGB: + return Subpixel_VRGB; + case Aurora::Platform::Output::Subpixel::VerticalBGR: + return Subpixel_VBGR; + default: + return Subpixel_None; + } +} + +QPixmap EglFSScreen::grabWindow(WId wid, int x, int y, int width, int height) const +{ + Q_UNUSED(wid); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(width); + Q_UNUSED(height); + return QPixmap(); +} diff --git a/src/plugins/platforms/eglfs/eglfsscreen.h b/src/plugins/platforms/eglfs/eglfsscreen.h new file mode 100644 index 00000000..bdb86329 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfsscreen.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include + +#include + +class QOpenGLContext; + +class EglFSCursor; +class EglFSWindow; + +class EglFSScreen + : public QObject + , public QPlatformScreen +{ + Q_OBJECT +public: + EglFSScreen(Aurora::Platform::Output *output); + ~EglFSScreen(); + + Aurora::Platform::Output *auroraOutput() const; + + QString name() const override; + + QString manufacturer() const override; + QString model() const override; + QString serialNumber() const override; + + QRect geometry() const override; + + qreal refreshRate() const override; + + int depth() const override; + QImage::Format format() const override; + + QSizeF physicalSize() const override; + QDpi logicalDpi() const override; + QDpi logicalBaseDpi() const override; + + Qt::ScreenOrientation nativeOrientation() const override; + Qt::ScreenOrientation orientation() const override; + + QPlatformCursor *cursor() const override; + + QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override; + + QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override; + +protected: + QPointer m_output; + EglFSCursor *m_cursor = nullptr; +}; diff --git a/src/plugins/platforms/eglfs/eglfswindow.cpp b/src/plugins/platforms/eglfs/eglfswindow.cpp new file mode 100644 index 00000000..4a638c46 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfswindow.cpp @@ -0,0 +1,207 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include + +#include +#include + +#include "eglfscategories.h" +#include "eglfsconfigchooser.h" +#include "eglfsintegration.h" +#include "eglfswindow.h" + +static WId newWinId() +{ + static WId id = 0; + + if (id == std::numeric_limits::max()) + qCWarning(gLcEglFS, "Out of window IDs"); + + return ++id; +} + +EglFSWindow::EglFSWindow(EglFSIntegration *integration, QWindow *window) + : QPlatformWindow(window) + , m_integration(integration) + , m_winId(newWinId()) + , m_format(window->requestedFormat()) +{ + m_window.reset(Aurora::Platform::auroraDeviceIntegration()->createWindow( + screen()->auroraOutput(), window)); +} + +EglFSWindow::~EglFSWindow() +{ + destroy(); +} + +Aurora::Platform::Window *EglFSWindow::auroraWindow() const +{ + return m_window.data(); +} + +bool EglFSWindow::create() +{ + if (m_created) + return true; + + m_created = true; + + // Windows are full screen + setGeometry(screen()->geometry()); + + if (!m_window->create()) + return false; + + if (!createSurface()) + return false; + + return true; +} + +WId EglFSWindow::winId() const +{ + return m_winId; +} + +EglFSScreen *EglFSWindow::screen() const +{ + return static_cast(QPlatformWindow::screen()); +} + +EGLNativeWindowType EglFSWindow::eglWindow() const +{ + return m_eglWindow; +} + +EGLSurface EglFSWindow::surface() const +{ + return m_eglSurface; +} + +QRect EglFSWindow::geometry() const +{ + return QPlatformWindow::geometry(); +} + +void EglFSWindow::setGeometry(const QRect &rect) +{ + const auto oldGeometry = geometry(); + + QPlatformWindow::setGeometry(rect); + + if (window()->isVisible() && rect.isValid()) { + // m_window->resize(rect.size()); + QWindowSystemInterface::handleGeometryChange(window(), geometry()); + } + + if (isExposed() && oldGeometry.size() != rect.size()) + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), rect.size())); +} + +void EglFSWindow::setVisible(bool visible) +{ + QPlatformWindow::setVisible(visible); +} + +void EglFSWindow::requestActivateWindow() +{ + auto *w = window(); + QWindowSystemInterface::handleWindowActivated(w); + QWindowSystemInterface::handleExposeEvent(w, QRect(QPoint(0, 0), w->geometry().size())); +} + +void EglFSWindow::raise() +{ + qCWarning(gLcEglFS, "Raising a window is not supported"); +} + +void EglFSWindow::lower() +{ + qCWarning(gLcEglFS, "Lowering a window is not supported"); +} + +void EglFSWindow::setOpacity(qreal level) +{ + Q_UNUSED(level) + qCWarning(gLcEglFS, "Setting opacity of a window is not supported"); +} + +void EglFSWindow::setMask(const QRegion ®ion) +{ + Q_UNUSED(region) + qCWarning(gLcEglFS, "Setting mask of a window is not supported"); +} + +QSurfaceFormat EglFSWindow::format() const +{ + return m_format; +} + +void EglFSWindow::invalidateSurface() +{ +} + +void EglFSWindow::resetSurface() +{ +} + +bool EglFSWindow::isRaster() const +{ + return false; +} + +void EglFSWindow::destroy() +{ + EGLDisplay eglDisplay = m_integration->display(); + + if (m_eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(eglDisplay, m_eglSurface); + m_eglSurface = EGL_NO_SURFACE; + } + + if (m_eglWindow) { + Aurora::Platform::auroraDeviceIntegration()->destroyNativeWindow(m_eglWindow); + m_eglWindow = 0; + } + + if (m_window) + m_window->destroy(); + + m_created = false; +} + +bool EglFSWindow::createSurface() +{ + EGLDisplay eglDisplay = m_integration->display(); + QSurfaceFormat platformFormat = Aurora::Platform::auroraDeviceIntegration()->surfaceFormatFor( + window()->requestedFormat()); + + EglFSConfigChooser chooser(eglDisplay); + chooser.setSurfaceType(Aurora::Platform::auroraDeviceIntegration()->surfaceType()); + chooser.setSurfaceFormat(platformFormat); + m_config = chooser.chooseConfig(); + + m_format = q_glFormatFromConfig(eglDisplay, m_config, platformFormat); + + const auto surfaceSize = screen()->geometry().size(); + m_eglWindow = Aurora::Platform::auroraDeviceIntegration()->createNativeWindow( + m_window.data(), surfaceSize, m_format); + if (!m_eglWindow) { + qCCritical(gLcEglFS, "Could not create native window for size %dx%d", surfaceSize.width(), + surfaceSize.height()); + return false; + } + + m_eglSurface = eglCreateWindowSurface(eglDisplay, m_config, m_eglWindow, nullptr); + if (Q_UNLIKELY(m_eglSurface == EGL_NO_SURFACE)) { + EGLint error = eglGetError(); + qCCritical(gLcEglFS, "Could not create EGL surface: error 0x%x\n", error); + return false; + } + + return true; +} diff --git a/src/plugins/platforms/eglfs/eglfswindow.h b/src/plugins/platforms/eglfs/eglfswindow.h new file mode 100644 index 00000000..a419d150 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfswindow.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include + +#include "eglfsscreen.h" + +class EglFSIntegration; + +class EglFSWindow : public QPlatformWindow +{ +public: + EglFSWindow(EglFSIntegration *integration, QWindow *window); + ~EglFSWindow(); + + Aurora::Platform::Window *auroraWindow() const; + + bool create(); + + WId winId() const override; + + EglFSScreen *screen() const override; + + EGLNativeWindowType eglWindow() const; + EGLSurface surface() const; + + QRect geometry() const override; + void setGeometry(const QRect &rect) override; + + void setVisible(bool visible) override; + void requestActivateWindow() override; + void raise() override; + void lower() override; + + void propagateSizeHints() override + { + } + + bool setKeyboardGrabEnabled(bool) override + { + return false; + } + bool setMouseGrabEnabled(bool) override + { + return false; + } + + void setOpacity(qreal level) override; + void setMask(const QRegion ®ion) override; + + QSurfaceFormat format() const override; + +#if QT_CONFIG(vulkan) + virtual void *vulkanSurfacePtr() + { + return nullptr; + } +#endif + + void invalidateSurface() override; + virtual void resetSurface(); + + bool isRaster() const; + +private: + QPointer m_integration; + WId m_winId = 0; + bool m_created = false; + QScopedPointer m_window; + + EGLSurface m_eglSurface = EGL_NO_SURFACE; + EGLConfig m_config = nullptr; + EGLNativeWindowType m_eglWindow = EGL_CAST(EGLNativeWindowType, 0); + QSurfaceFormat m_format; + + void destroy(); + bool createSurface(); +};