Source code
Revision control
Copy as Markdown
// 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
// Qt-Security score:significant reason:default
#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/qgenericunixtheme_p.h>
#include <qpa/qplatformservices.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);
qCDebug(qLcVkKhrDisplay) << "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();
const auto windows = QGuiApplication::allWindows();
for (QWindow *window : windows) {
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 QPlatformServices;
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