QPA offscreen: make platform plugin configurable

Add ability to load a json config file containing screen
configuration at startup. The config file location
is specified using platform options:

    -platform offscfreen:configfile=/path/to/file

Config file format example:
{
    “screens”: [
        {
        "name", "screen-1",
        "x", 0,
        "y", 0,
        "width", 640,
        "height", 480,
        "dpi", 96,
        "dpr", 1,
        },
        …
    ]
}

Change-Id: Iac21aaafa6d0f361bdd6f6e9168b7e68db6ae011
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Morten Johan Sørvig 2020-02-03 23:03:29 +01:00
parent 0ab89881c5
commit f4a66e5c4c
7 changed files with 144 additions and 20 deletions

View File

@ -53,9 +53,8 @@ public:
QPlatformIntegration *QOffscreenIntegrationPlugin::create(const QString& system, const QStringList& paramList)
{
Q_UNUSED(paramList);
if (!system.compare(QLatin1String("offscreen"), Qt::CaseInsensitive))
return QOffscreenIntegration::createOffscreenIntegration();
return QOffscreenIntegration::createOffscreenIntegration(paramList);
return nullptr;
}

View File

@ -38,8 +38,10 @@
****************************************************************************/
#include "qoffscreencommon.h"
#include "qoffscreenintegration.h"
#include "qoffscreenwindow.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
@ -50,6 +52,12 @@ QT_BEGIN_NAMESPACE
QPlatformWindow *QOffscreenScreen::windowContainingCursor = nullptr;
QList<QPlatformScreen *> QOffscreenScreen::virtualSiblings() const
{
return m_integration->screens();
}
class QOffscreenCursor : public QPlatformCursor
{
public:
@ -93,9 +101,10 @@ private:
QPoint m_pos;
};
QOffscreenScreen::QOffscreenScreen()
QOffscreenScreen::QOffscreenScreen(const QOffscreenIntegration *integration)
: m_geometry(0, 0, 800, 600)
, m_cursor(new QOffscreenCursor)
, m_integration(integration)
{
}

View File

@ -54,23 +54,34 @@
QT_BEGIN_NAMESPACE
class QOffscreenIntegration;
class QOffscreenScreen : public QPlatformScreen
{
public:
QOffscreenScreen();
QOffscreenScreen(const QOffscreenIntegration *integration);
QRect geometry() const override { return m_geometry; }
int depth() const override { return 32; }
QImage::Format format() const override { return QImage::Format_RGB32; }
QDpi logicalDpi() const override { return QDpi(m_logicalDpi, m_logicalDpi); }
QDpi logicalBaseDpi() const override { return QDpi(m_logicalBaseDpi, m_logicalBaseDpi); }
qreal devicePixelRatio() const override { return m_dpr; }
QString name() const override { return m_name; }
QPlatformCursor *cursor() const override { return m_cursor.data(); }
QList<QPlatformScreen *> virtualSiblings() const override;
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
static QPlatformWindow *windowContainingCursor;
public:
QString m_name;
QRect m_geometry;
int m_logicalDpi = 96;
int m_logicalBaseDpi= 96;
qreal m_dpr = 1;
QScopedPointer<QPlatformCursor> m_cursor;
const QOffscreenIntegration *m_integration;
};
#if QT_CONFIG(draganddrop)

View File

@ -54,6 +54,11 @@
#include <QtCore/private/qeventdispatcher_win_p.h>
#endif
#include <QtCore/qfile.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonvalue.h>
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatforminputcontextfactory_p.h>
@ -104,14 +109,98 @@ QOffscreenIntegration::QOffscreenIntegration()
m_drag.reset(new QOffscreenDrag);
#endif
m_services.reset(new QPlatformServices);
QWindowSystemInterface::handleScreenAdded(new QOffscreenScreen);
}
QOffscreenIntegration::~QOffscreenIntegration()
{
}
/*
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:
./myapp -platform offscreen:configfile=/path/to/config.json
The supported top-level config keys are:
{
"synchronousWindowSystemEvents": <bool>
"windowFrameMargins": <bool>,
"screens": [<screens>],
}
Screen:
{
"name" : string,
"x": int,
"y": int,
"width": int,
"height": int,
"logicalDpi": int,
"logicalBaseDpi": int,
"dpr": double,
}
*/
void QOffscreenIntegration::configure(const QStringList& paramList)
{
// Use config file configuring platform plugin, if one was specified
bool hasConfigFile = false;
QString configFilePath;
for (const QString &param : paramList) {
// Look for "configfile=/path/to/file/"
QString configPrefix(QLatin1String("configfile="));
if (param.startsWith(configPrefix)) {
hasConfigFile = true;
configFilePath= param.mid(configPrefix.length());
}
}
// 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;
}
// Read config file
if (configFilePath.isEmpty())
qFatal("Missing file path for -configfile platform option");
QFile configFile(configFilePath);
if (!configFile.exists())
qFatal("Could not find platform config file %s", qPrintable(configFilePath));
if (!configFile.open(QIODevice::ReadOnly))
qFatal("Could not open platform config file for reading %s, %s", qPrintable(configFilePath), qPrintable(configFile.errorString()));
QByteArray json = configFile.readAll();
QJsonParseError error;
QJsonDocument config = QJsonDocument::fromJson(json, &error);
if (config.isNull())
qFatal("Platform config file parse error: %s", qPrintable(error.errorString()));
// Apply configuration (create screens)
bool synchronousWindowSystemEvents = config["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");
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);
m_screens.append(offscreenScreen);
QWindowSystemInterface::handleScreenAdded(offscreenScreen);
}
}
void QOffscreenIntegration::initialize()
{
m_inputContext.reset(QPlatformInputContextFactory::create());
@ -134,7 +223,7 @@ bool QOffscreenIntegration::hasCapability(QPlatformIntegration::Capability cap)
QPlatformWindow *QOffscreenIntegration::createPlatformWindow(QWindow *window) const
{
Q_UNUSED(window);
QPlatformWindow *w = new QOffscreenWindow(window);
QPlatformWindow *w = new QOffscreenWindow(window, m_windowFrameMarginsEnabled);
w->requestActivateWindow();
return w;
}
@ -203,14 +292,25 @@ QPlatformServices *QOffscreenIntegration::services() const
return m_services.data();
}
QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration()
QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration(const QStringList& paramList)
{
QOffscreenIntegration *offscreenIntegration = nullptr;
#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
QByteArray glx = qgetenv("QT_QPA_OFFSCREEN_NO_GLX");
if (glx.isEmpty())
return new QOffscreenX11Integration;
offscreenIntegration = new QOffscreenX11Integration;
#else
offscreenIntegration = new QOffscreenIntegration;
#endif
return new QOffscreenIntegration;
offscreenIntegration->configure(paramList);
return offscreenIntegration;
}
QList<QPlatformScreen *> QOffscreenIntegration::screens() const
{
return m_screens;
}
QT_END_NAMESPACE

View File

@ -55,6 +55,7 @@ public:
QOffscreenIntegration();
~QOffscreenIntegration();
void configure(const QStringList& paramList);
void initialize() override;
bool hasCapability(QPlatformIntegration::Capability cap) const override;
@ -73,8 +74,9 @@ public:
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
static QOffscreenIntegration *createOffscreenIntegration();
static QOffscreenIntegration *createOffscreenIntegration(const QStringList& paramList);
QList<QPlatformScreen *> screens() const;
private:
QScopedPointer<QPlatformFontDatabase> m_fontDatabase;
#if QT_CONFIG(draganddrop)
@ -82,6 +84,8 @@ private:
#endif
QScopedPointer<QPlatformInputContext> m_inputContext;
QScopedPointer<QPlatformServices> m_services;
QList<QPlatformScreen *> m_screens;
bool m_windowFrameMarginsEnabled = true;
};
QT_END_NAMESPACE

View File

@ -47,18 +47,18 @@
QT_BEGIN_NAMESPACE
QOffscreenWindow::QOffscreenWindow(QWindow *window)
QOffscreenWindow::QOffscreenWindow(QWindow *window, bool frameMarginsEnabled)
: QPlatformWindow(window)
, m_positionIncludesFrame(false)
, m_visible(false)
, m_pendingGeometryChangeOnShow(true)
, m_frameMarginsRequested(frameMarginsEnabled)
{
if (window->windowState() == Qt::WindowNoState)
setGeometry(window->geometry());
else
if (window->windowState() == Qt::WindowNoState) {
setGeometry(windowGeometry());
} else {
setWindowState(window->windowStates());
QWindowSystemInterface::flushWindowSystemEvents();
}
static WId counter = 0;
m_winId = ++counter;
@ -80,7 +80,7 @@ void QOffscreenWindow::setGeometry(const QRect &rect)
m_positionIncludesFrame = qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameInclusive;
setFrameMarginsEnabled(true);
setFrameMarginsEnabled(m_frameMarginsRequested);
setGeometryImpl(rect);
m_normalGeometry = geometry();
@ -168,7 +168,7 @@ void QOffscreenWindow::setFrameMarginsEnabled(bool enabled)
void QOffscreenWindow::setWindowState(Qt::WindowStates state)
{
setFrameMarginsEnabled(!(state & Qt::WindowFullScreen));
setFrameMarginsEnabled(m_frameMarginsRequested && !(state & Qt::WindowFullScreen));
m_positionIncludesFrame = false;
if (state & Qt::WindowMinimized)

View File

@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
class QOffscreenWindow : public QPlatformWindow
{
public:
QOffscreenWindow(QWindow *window);
QOffscreenWindow(QWindow *window, bool frameMarginsEnabled);
~QOffscreenWindow();
void setGeometry(const QRect &rect) override;
@ -74,6 +74,7 @@ private:
bool m_positionIncludesFrame;
bool m_visible;
bool m_pendingGeometryChangeOnShow;
bool m_frameMarginsRequested;
WId m_winId;
static QHash<WId, QOffscreenWindow *> m_windowForWinIdHash;