diff options
Diffstat (limited to 'src/plugins/platforms/offscreen/qoffscreenintegration.cpp')
-rw-r--r-- | src/plugins/platforms/offscreen/qoffscreenintegration.cpp | 262 |
1 files changed, 182 insertions, 80 deletions
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index 310e71ae13..ea8042928a 100644 --- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 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 "qoffscreenintegration.h" #include "qoffscreenwindow.h" @@ -74,6 +38,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class QCoreTextFontEngine; template <typename BaseEventDispatcher> @@ -93,7 +59,7 @@ public: } }; -QOffscreenIntegration::QOffscreenIntegration() +QOffscreenIntegration::QOffscreenIntegration(const QStringList& paramList) { #if defined(Q_OS_UNIX) #if defined(Q_OS_MAC) @@ -109,17 +75,24 @@ QOffscreenIntegration::QOffscreenIntegration() m_drag.reset(new QOffscreenDrag); #endif m_services.reset(new QPlatformServices); + + QJsonObject config = resolveConfigFileConfiguration(paramList).value_or(defaultConfiguration()); + setConfiguration(config); } QOffscreenIntegration::~QOffscreenIntegration() { - for (auto screen : std::as_const(m_screens)) - QWindowSystemInterface::handleScreenRemoved(screen); + while (!m_screens.isEmpty()) + QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); } /* - The offscren platform plugin is configurable with a JSON configuration - file. Write the config to disk and pass the file path as a platform argument: + The offscren platform plugin is configurable with a JSON configuration. + The confiuration can be provided either from a file on disk on startup, + or at by calling setConfiguration(). + + To provide a configuration on startuip, write the config to disk and pass + the file path as a platform argument: ./myapp -platform offscreen:configfile=/path/to/config.json @@ -130,9 +103,9 @@ QOffscreenIntegration::~QOffscreenIntegration() "screens": [<screens>], } - Screen: + "screens" is an array of: { - "name" : string, + "name": string, "x": int, "y": int, "width": int, @@ -142,27 +115,41 @@ QOffscreenIntegration::~QOffscreenIntegration() "dpr": double, } */ -void QOffscreenIntegration::configure(const QStringList& paramList) + +QJsonObject QOffscreenIntegration::defaultConfiguration() const +{ + const auto defaultScreen = QJsonObject { + {"name", ""}, + {"x", 0}, + {"y", 0}, + {"width", 800}, + {"height", 800}, + {"logicalDpi", 96}, + {"logicalBaseDpi", 96}, + {"dpr", 1.0}, + }; + const auto defaultConfiguration = QJsonObject { + {"synchronousWindowSystemEvents", false}, + {"windowFrameMargins", true}, + {"screens", QJsonArray { defaultScreen } }, + }; + return defaultConfiguration; +} + +std::optional<QJsonObject> QOffscreenIntegration::resolveConfigFileConfiguration(const QStringList& paramList) const { - // Use config file configuring platform plugin, if one was specified bool hasConfigFile = false; QString configFilePath; for (const QString ¶m : paramList) { // Look for "configfile=/path/to/file/" - QString configPrefix(QLatin1String("configfile=")); + QString configPrefix("configfile="_L1); if (param.startsWith(configPrefix)) { hasConfigFile = true; - configFilePath= param.mid(configPrefix.length()); + configFilePath = param.mid(configPrefix.size()); } } - - // Create the default screen if there was no config file - if (!hasConfigFile) { - QOffscreenScreen *offscreenScreen = new QOffscreenScreen(this); - m_screens.append(offscreenScreen); - QWindowSystemInterface::handleScreenAdded(offscreenScreen); - return; - } + if (!hasConfigFile) + return std::nullopt; // Read config file if (configFilePath.isEmpty()) @@ -179,28 +166,145 @@ void QOffscreenIntegration::configure(const QStringList& paramList) if (config.isNull()) qFatal("Platform config file parse error: %s", qPrintable(error.errorString())); - // Apply configuration (create screens) - bool synchronousWindowSystemEvents = config["synchronousWindowSystemEvents"].toBool(false); + return config.object(); +} + + +void QOffscreenIntegration::setConfiguration(const QJsonObject &configuration) +{ + // Apply the new configuration, diffing against the current m_configuration + + const bool synchronousWindowSystemEvents = configuration["synchronousWindowSystemEvents"].toBool( + m_configuration["synchronousWindowSystemEvents"].toBool(false)); QWindowSystemInterface::setSynchronousWindowSystemEvents(synchronousWindowSystemEvents); - m_windowFrameMarginsEnabled = config["windowFrameMargins"].toBool(true); - QJsonArray screens = config["screens"].toArray(); - for (QJsonValue screenValue : screens) { - QJsonObject screen = screenValue.toObject(); - if (screen.isEmpty()) { - qWarning("QOffscreenIntegration::initializeWithPlatformArguments: empty screen object"); + + m_windowFrameMarginsEnabled = configuration["windowFrameMargins"].toBool( + m_configuration["windowFrameMargins"].toBool(true)); + + // Diff screens array, using the screen name as the screen identity. + QJsonArray currentScreens = m_configuration["screens"].toArray(); + QJsonArray newScreens = configuration["screens"].toArray(); + + auto getScreenNames = [](const QJsonArray &screens) -> QList<QString> { + QList<QString> names; + for (QJsonValue screen : screens) { + names.append(screen["name"].toString()); + }; + std::sort(names.begin(), names.end()); + return names; + }; + + auto currentNames = getScreenNames(currentScreens); + auto newNames = getScreenNames(newScreens); + + QList<QString> present; + std::set_intersection(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(), + std::inserter(present, present.begin())); + QList<QString> added; + std::set_difference(newNames.begin(), newNames.end(), currentNames.begin(), currentNames.end(), + std::inserter(added, added.begin())); + QList<QString> removed; + std::set_difference(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(), + std::inserter(removed, removed.begin())); + + auto platformScreenByName = [](const QString &name, QList<QOffscreenScreen *> screens) -> QOffscreenScreen * { + for (QOffscreenScreen *screen : screens) { + if (screen->m_name == name) + return screen; + } + Q_UNREACHABLE(); + }; + + auto screenConfigByName = [](const QString &name, QJsonArray screenConfigs) -> QJsonValue { + for (QJsonValue screenConfig : screenConfigs) { + if (screenConfig["name"].toString() == name) + return screenConfig; + } + Q_UNREACHABLE(); + }; + + auto geometryFromConfig = [](const QJsonObject &config) -> QRect { + return QRect(config["x"].toInt(0), config["y"].toInt(0), config["width"].toInt(640), config["height"].toInt(480)); + }; + + // Remove removed screens + for (const QString &remove : removed) { + QOffscreenScreen *screen = platformScreenByName(remove, m_screens); + m_screens.removeAll(screen); + QWindowSystemInterface::handleScreenRemoved(screen); + } + + // Add new screens + for (const QString &add : added) { + QJsonValue configValue = screenConfigByName(add, newScreens); + QJsonObject config = configValue.toObject(); + if (config.isEmpty()) { + qWarning("empty screen object"); continue; } QOffscreenScreen *offscreenScreen = new QOffscreenScreen(this); - offscreenScreen->m_name = screen["name"].toString(); - offscreenScreen->m_geometry = QRect(screen["x"].toInt(0), screen["y"].toInt(0), - screen["width"].toInt(640), screen["height"].toInt(480)); - offscreenScreen->m_logicalDpi = screen["logicalDpi"].toInt(96); - offscreenScreen->m_logicalBaseDpi = screen["logicalBaseDpi"].toInt(96); - offscreenScreen->m_dpr = screen["dpr"].toDouble(1.0); - + offscreenScreen->m_name = config["name"].toString(); + offscreenScreen->m_geometry = geometryFromConfig(config); + offscreenScreen->m_logicalDpi = config["logicalDpi"].toInt(96); + offscreenScreen->m_logicalBaseDpi = config["logicalBaseDpi"].toInt(96); + offscreenScreen->m_dpr = config["dpr"].toDouble(1.0); m_screens.append(offscreenScreen); QWindowSystemInterface::handleScreenAdded(offscreenScreen); } + + // Update present screens + for (const QString &pres : present) { + QOffscreenScreen *screen = platformScreenByName(pres, m_screens); + Q_ASSERT(screen); + QJsonObject currentConfig = screenConfigByName(pres, currentScreens).toObject(); + QJsonObject newConfig = screenConfigByName(pres, newScreens).toObject(); + + // Name can't change, because it'd be a different screen + Q_ASSERT(currentConfig["name"] == newConfig["name"]); + + // Geometry + QRect currentGeomtry = geometryFromConfig(currentConfig); + QRect newGeomtry = geometryFromConfig(newConfig); + if (currentGeomtry != newGeomtry) { + screen->m_geometry = newGeomtry; + QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), newGeomtry, newGeomtry); + } + + // logical DPI + int currentLogicalDpi = currentConfig["logicalDpi"].toInt(96); + int newLogicalDpi = newConfig["logicalDpi"].toInt(96); + if (currentLogicalDpi != newLogicalDpi) { + screen->m_logicalDpi = newLogicalDpi; + QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), newLogicalDpi, newLogicalDpi); + } + + // The base DPI is more of a platform constant, and should not change, and + // there is no handleChange function for it. Print a warning. + int currentLogicalBaseDpi = currentConfig["logicalBaseDpi"].toInt(96); + int newLogicalBaseDpi = newConfig["logicalBaseDpi"].toInt(96); + if (currentLogicalBaseDpi != newLogicalBaseDpi) { + screen->m_logicalBaseDpi = newLogicalBaseDpi; + qWarning("You ain't supposed to change logicalBaseDpi - its a platform constant. Qt may not react to the change"); + } + + // DPR. There is also no handleChange function in Qt at this point, instead + // the new DPR value will be used during the next repaint. We could repaint + // all windows here, but don't. Print a warning. + double currentDpr = currentConfig["dpr"].toDouble(1); + double newDpr = newConfig["dpr"].toDouble(1); + if (currentDpr != newDpr) { + screen->m_dpr = newDpr; + qWarning("DPR change notifications is not implemented - Qt may not react to the change"); + } + } + + // Now the new configuration is the current configuration + m_configuration = configuration; +} + +QJsonObject QOffscreenIntegration::configuration() const +{ + return m_configuration; } void QOffscreenIntegration::initialize() @@ -250,7 +354,7 @@ QAbstractEventDispatcher *QOffscreenIntegration::createEventDispatcher() const QPlatformNativeInterface *QOffscreenIntegration::nativeInterface() const { if (!m_nativeInterface) - m_nativeInterface.reset(new QOffscreenPlatformNativeInterface); + m_nativeInterface.reset(new QOffscreenPlatformNativeInterface(const_cast<QOffscreenIntegration*>(this))); return m_nativeInterface.get(); } @@ -281,8 +385,8 @@ public: virtual const QFont *font(Font type = SystemFont) const override { - static QFont systemFont(QLatin1String("Sans Serif"), 9); - static QFont fixedFont(QLatin1String("monospace"), 9); + static QFont systemFont("Sans Serif"_L1, 9); + static QFont fixedFont("monospace"_L1, 9); switch (type) { case QPlatformTheme::SystemFont: return &systemFont; @@ -323,17 +427,15 @@ QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration(const Q #if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2) QByteArray glx = qgetenv("QT_QPA_OFFSCREEN_NO_GLX"); if (glx.isEmpty()) - offscreenIntegration = new QOffscreenX11Integration; + offscreenIntegration = new QOffscreenX11Integration(paramList); #endif if (!offscreenIntegration) - offscreenIntegration = new QOffscreenIntegration; - - offscreenIntegration->configure(paramList); + offscreenIntegration = new QOffscreenIntegration(paramList); return offscreenIntegration; } -QList<QPlatformScreen *> QOffscreenIntegration::screens() const +QList<QOffscreenScreen *> QOffscreenIntegration::screens() const { return m_screens; } |