diff options
Diffstat (limited to 'src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp')
-rw-r--r-- | src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp new file mode 100644 index 0000000000..502c2518f2 --- /dev/null +++ b/src/plugins/platforms/vkkhrdisplay/qvkkhrdisplayintegration.cpp @@ -0,0 +1,315 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qvkkhrdisplayintegration.h" +#include "qvkkhrdisplayvulkaninstance.h" + +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qwindowsysteminterface.h> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qgenericunixeventdispatcher_p.h> +#include <QtGui/private/qgenericunixfontdatabase_p.h> +#include <QtGui/private/qgenericunixthemes_p.h> +#include <QtGui/private/qgenericunixservices_p.h> + +#include <QtFbSupport/private/qfbvthandler_p.h> + +#if QT_CONFIG(libinput) +#include <QtInputSupport/private/qlibinputhandler_p.h> +#endif + +#if QT_CONFIG(evdev) +#include <QtInputSupport/private/qevdevmousemanager_p.h> +#include <QtInputSupport/private/qevdevkeyboardmanager_p.h> +#include <QtInputSupport/private/qevdevtouchmanager_p.h> +#endif + +#if QT_CONFIG(tslib) +#include <QtInputSupport/private/qtslib_p.h> +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +class QVkKhrDisplayScreen : public QPlatformScreen +{ +public: + QRect geometry() const override { return m_geometry; } + int depth() const override { return m_depth; } + QImage::Format format() const override { return m_format; } + void setVk(QVkKhrDisplayVulkanInstance *inst); + +private: + QVkKhrDisplayVulkanInstance *m_vk = nullptr; + QRect m_geometry; + int m_depth = 32; + QImage::Format m_format = QImage::Format_ARGB32_Premultiplied; + friend class QVkKhrDisplayIntegration; +}; + +void QVkKhrDisplayScreen::setVk(QVkKhrDisplayVulkanInstance *inst) +{ + m_vk = inst; + m_geometry = QRect(QPoint(0, 0), m_vk->displaySize()); + QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry, m_geometry); + qDebug() << "Screen will report geometry" << m_geometry; + + // Thanks to this deferred screen setup, a QWindow with a size based on the + // dummy screen size may already exist. Try to resize it. + QScreen *thisScreen = screen(); + for (QWindow *window : QGuiApplication::allWindows()) { + if (window->isTopLevel() && window->screen() == thisScreen) + window->handle()->setGeometry(QRect()); // set fullscreen geometry + } +} + +class QVkKhrDisplayWindow : public QPlatformWindow +{ +public: + QVkKhrDisplayWindow(QWindow *window) : QPlatformWindow(window) { } + ~QVkKhrDisplayWindow(); + + void *vulkanSurfacePtr(); + + void setGeometry(const QRect &r) override; + +private: + VkSurfaceKHR m_surface = VK_NULL_HANDLE; +}; + +QVkKhrDisplayWindow::~QVkKhrDisplayWindow() +{ + if (m_surface) { + QVulkanInstance *inst = window()->vulkanInstance(); + if (inst) + static_cast<QVkKhrDisplayVulkanInstance *>(inst->handle())->destroySurface(m_surface); + } +} + +void *QVkKhrDisplayWindow::vulkanSurfacePtr() +{ + if (m_surface) + return &m_surface; + + QVulkanInstance *inst = window()->vulkanInstance(); + if (!inst) { + qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?"); + return nullptr; + } + QVkKhrDisplayVulkanInstance *vkdinst = static_cast<QVkKhrDisplayVulkanInstance *>(inst->handle()); + m_surface = vkdinst->createSurface(window()); + + return &m_surface; +} + +void QVkKhrDisplayWindow::setGeometry(const QRect &) +{ + // We only support full-screen windows + QRect rect(screen()->availableGeometry()); + QWindowSystemInterface::handleGeometryChange(window(), rect); + QPlatformWindow::setGeometry(rect); + + const QRect lastReportedGeometry = qt_window_private(window())->geometry; + if (rect != lastReportedGeometry) + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size())); +} + +// does not actually support raster content, just paint into a QImage and that's it for now +class QVkKhrDisplayBackingStore : public QPlatformBackingStore +{ +public: + QVkKhrDisplayBackingStore(QWindow *window) : QPlatformBackingStore(window) { } + + QPaintDevice *paintDevice() override { return &m_image; } + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override { + Q_UNUSED(window); + Q_UNUSED(region); + Q_UNUSED(offset); + } + void resize(const QSize &size, const QRegion &staticContents) override { + Q_UNUSED(staticContents); + QImage::Format format = QGuiApplication::primaryScreen()->handle()->format(); + if (m_image.size() != size) + m_image = QImage(size, format); + } + +private: + QImage m_image; +}; + +QVkKhrDisplayIntegration::QVkKhrDisplayIntegration(const QStringList ¶meters) +{ + Q_UNUSED(parameters); +} + +QVkKhrDisplayIntegration::~QVkKhrDisplayIntegration() +{ + QWindowSystemInterface::handleScreenRemoved(m_primaryScreen); + delete m_services; + delete m_fontDatabase; + delete m_vtHandler; +} + +bool QVkKhrDisplayIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; + case WindowManagement: return false; + default: return QPlatformIntegration::hasCapability(cap); + } +} + +void QVkKhrDisplayIntegration::initialize() +{ + m_primaryScreen = new QVkKhrDisplayScreen; + + // The real values are only known when the QVulkanInstance initializes, use + // dummy values until then. + m_primaryScreen->m_geometry = QRect(0, 0, 1920, 1080); + m_primaryScreen->m_depth = 32; + m_primaryScreen->m_format = QImage::Format_ARGB32_Premultiplied; + + QWindowSystemInterface::handleScreenAdded(m_primaryScreen); + + m_inputContext = QPlatformInputContextFactory::create(); + + m_vtHandler = new QFbVtHandler; + + if (!qEnvironmentVariableIntValue("QT_QPA_DISABLE_INPUT")) + createInputHandlers(); +} + +QPlatformFontDatabase *QVkKhrDisplayIntegration::fontDatabase() const +{ + if (!m_fontDatabase) + m_fontDatabase = new QGenericUnixFontDatabase; + + return m_fontDatabase; +} + +QPlatformServices *QVkKhrDisplayIntegration::services() const +{ + if (!m_services) + m_services = new QGenericUnixServices; + + return m_services; +} + +QPlatformInputContext *QVkKhrDisplayIntegration::inputContext() const +{ + return m_inputContext; +} + +QPlatformTheme *QVkKhrDisplayIntegration::createPlatformTheme(const QString &name) const +{ + return QGenericUnixTheme::createUnixTheme(name); +} + +QPlatformNativeInterface *QVkKhrDisplayIntegration::nativeInterface() const +{ + return const_cast<QVkKhrDisplayIntegration *>(this); +} + +QPlatformWindow *QVkKhrDisplayIntegration::createPlatformWindow(QWindow *window) const +{ + if (window->surfaceType() != QSurface::VulkanSurface) { + qWarning("vkkhrdisplay platform plugin only supports QWindow with surfaceType == VulkanSurface"); + // Assume VulkanSurface, better than crashing. Consider e.g. an autotest + // creating a default QWindow just to have something to be used with + // QRhi's Null backend. Continuing to set up a Vulkan window (even + // though the request was Raster or something) is better than failing to + // create a platform window, and may even be sufficient in some cases. + } + + QVkKhrDisplayWindow *w = new QVkKhrDisplayWindow(window); + w->setGeometry(QRect()); // set fullscreen geometry + w->requestActivateWindow(); + return w; +} + +QPlatformBackingStore *QVkKhrDisplayIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QVkKhrDisplayBackingStore(window); +} + +QAbstractEventDispatcher *QVkKhrDisplayIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + +void QVkKhrDisplayIntegration::handleInstanceCreated(QVkKhrDisplayVulkanInstance *inst, void *userData) +{ + QVkKhrDisplayIntegration *self = static_cast<QVkKhrDisplayIntegration *>(userData); + self->m_primaryScreen->setVk(inst); +} + +QPlatformVulkanInstance *QVkKhrDisplayIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const +{ + QVkKhrDisplayVulkanInstance *inst = new QVkKhrDisplayVulkanInstance(instance); + inst->setCreatedCallback(handleInstanceCreated, const_cast<QVkKhrDisplayIntegration *>(this)); + return inst; +} + +enum ResourceType { + VkSurface +}; + +static int resourceType(const QByteArray &key) +{ + static const QByteArray names[] = { // match ResourceType + QByteArrayLiteral("vksurface") + }; + const QByteArray *end = names + sizeof(names) / sizeof(names[0]); + const QByteArray *result = std::find(names, end, key); + if (result == end) + result = std::find(names, end, key.toLower()); + return int(result - names); +} + +void *QVkKhrDisplayIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + void *result = nullptr; + + switch (resourceType(resource)) { + case VkSurface: + if (window && window->handle() && window->surfaceType() == QSurface::VulkanSurface) + result = static_cast<QVkKhrDisplayWindow *>(window->handle())->vulkanSurfacePtr(); + break; + default: + break; + } + + return result; +} + +void QVkKhrDisplayIntegration::createInputHandlers() +{ +#if QT_CONFIG(libinput) + if (!qEnvironmentVariableIntValue("QT_QPA_NO_LIBINPUT")) { + new QLibInputHandler("libinput"_L1, QString()); + return; + } +#endif + +#if QT_CONFIG(tslib) + bool useTslib = qEnvironmentVariableIntValue("QT_QPA_TSLIB"); + if (useTslib) + new QTsLibMouseHandler("TsLib"_L1, QString()); +#endif + +#if QT_CONFIG(evdev) + new QEvdevKeyboardManager("EvdevKeyboard"_L1, QString(), this); + new QEvdevMouseManager("EvdevMouse"_L1, QString(), this); +#if QT_CONFIG(tslib) + if (!useTslib) +#endif + new QEvdevTouchManager("EvdevTouch"_L1, QString() /* spec */, this); +#endif +} + +QT_END_NAMESPACE |