Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qoffscreenintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qoffscreenwindow.h"
6#include "qoffscreencommon.h"
7
8#if defined(Q_OS_UNIX)
9#include <QtGui/private/qgenericunixeventdispatcher_p.h>
10#if defined(Q_OS_MAC)
11#include <qpa/qplatformfontdatabase.h>
12#include <QtGui/private/qcoretextfontdatabase_p.h>
13#else
14#include <QtGui/private/qgenericunixfontdatabase_p.h>
15#endif
16#elif defined(Q_OS_WIN)
17#include <QtGui/private/qfreetypefontdatabase_p.h>
18#include <QtCore/private/qeventdispatcher_win_p.h>
19#endif
20
21#include <QtCore/qfile.h>
22#include <QtCore/qjsonarray.h>
23#include <QtCore/qjsondocument.h>
24#include <QtCore/qjsonobject.h>
25#include <QtCore/qjsonvalue.h>
26#include <QtGui/private/qpixmap_raster_p.h>
27#include <QtGui/private/qguiapplication_p.h>
28#include <qpa/qplatforminputcontextfactory_p.h>
29#include <qpa/qplatforminputcontext.h>
30#include <qpa/qplatformtheme.h>
31#include <qpa/qwindowsysteminterface.h>
32
33#include <qpa/qplatformservices.h>
34
35#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
37#endif
38
40
41using namespace Qt::StringLiterals;
42
44
45template <typename BaseEventDispatcher>
46class QOffscreenEventDispatcher : public BaseEventDispatcher
47{
48public:
49 explicit QOffscreenEventDispatcher(QObject *parent = nullptr)
50 : BaseEventDispatcher(parent)
51 {
52 }
53
54 bool processEvents(QEventLoop::ProcessEventsFlags flags) override
55 {
56 bool didSendEvents = BaseEventDispatcher::processEvents(flags);
57
59 }
60};
61
63{
64#if defined(Q_OS_UNIX)
65#if defined(Q_OS_MAC)
66 m_fontDatabase.reset(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>);
67#else
69#endif
70#elif defined(Q_OS_WIN)
72#endif
73
74#if QT_CONFIG(draganddrop)
75 m_drag.reset(new QOffscreenDrag);
76#endif
78
81}
82
88
89/*
90 The offscren platform plugin is configurable with a JSON configuration.
91 The confiuration can be provided either from a file on disk on startup,
92 or at by calling setConfiguration().
93
94 To provide a configuration on startuip, write the config to disk and pass
95 the file path as a platform argument:
96
97 ./myapp -platform offscreen:configfile=/path/to/config.json
98
99 The supported top-level config keys are:
100 {
101 "synchronousWindowSystemEvents": <bool>
102 "windowFrameMargins": <bool>,
103 "screens": [<screens>],
104 }
105
106 "screens" is an array of:
107 {
108 "name": string,
109 "x": int,
110 "y": int,
111 "width": int,
112 "height": int,
113 "logicalDpi": int,
114 "logicalBaseDpi": int,
115 "dpr": double,
116 }
117*/
118
120{
121 const auto defaultScreen = QJsonObject {
122 {"name", ""},
123 {"x", 0},
124 {"y", 0},
125 {"width", 800},
126 {"height", 800},
127 {"logicalDpi", 96},
128 {"logicalBaseDpi", 96},
129 {"dpr", 1.0},
130 };
131 const auto defaultConfiguration = QJsonObject {
132 {"synchronousWindowSystemEvents", false},
133 {"windowFrameMargins", true},
134 {"screens", QJsonArray { defaultScreen } },
135 };
137}
138
139std::optional<QJsonObject> QOffscreenIntegration::resolveConfigFileConfiguration(const QStringList& paramList) const
140{
141 bool hasConfigFile = false;
142 QString configFilePath;
143 for (const QString &param : paramList) {
144 // Look for "configfile=/path/to/file/"
145 QString configPrefix("configfile="_L1);
146 if (param.startsWith(configPrefix)) {
147 hasConfigFile = true;
148 configFilePath = param.mid(configPrefix.size());
149 }
150 }
151 if (!hasConfigFile)
152 return std::nullopt;
153
154 // Read config file
155 if (configFilePath.isEmpty())
156 qFatal("Missing file path for -configfile platform option");
157 QFile configFile(configFilePath);
158 if (!configFile.exists())
159 qFatal("Could not find platform config file %s", qPrintable(configFilePath));
160 if (!configFile.open(QIODevice::ReadOnly))
161 qFatal("Could not open platform config file for reading %s, %s", qPrintable(configFilePath), qPrintable(configFile.errorString()));
162
163 QByteArray json = configFile.readAll();
166 if (config.isNull())
167 qFatal("Platform config file parse error: %s", qPrintable(error.errorString()));
168
169 return config.object();
170}
171
172
174{
175 // Apply the new configuration, diffing against the current m_configuration
176
177 const bool synchronousWindowSystemEvents = configuration["synchronousWindowSystemEvents"].toBool(
178 m_configuration["synchronousWindowSystemEvents"].toBool(false));
180
181 m_windowFrameMarginsEnabled = configuration["windowFrameMargins"].toBool(
182 m_configuration["windowFrameMargins"].toBool(true));
183
184 // Diff screens array, using the screen name as the screen identity.
185 QJsonArray currentScreens = m_configuration["screens"].toArray();
186 QJsonArray newScreens = configuration["screens"].toArray();
187
188 auto getScreenNames = [](const QJsonArray &screens) -> QList<QString> {
189 QList<QString> names;
190 for (QJsonValue screen : screens) {
191 names.append(screen["name"].toString());
192 };
193 std::sort(names.begin(), names.end());
194 return names;
195 };
196
197 auto currentNames = getScreenNames(currentScreens);
198 auto newNames = getScreenNames(newScreens);
199
200 QList<QString> present;
201 std::set_intersection(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(),
202 std::inserter(present, present.begin()));
203 QList<QString> added;
204 std::set_difference(newNames.begin(), newNames.end(), currentNames.begin(), currentNames.end(),
205 std::inserter(added, added.begin()));
206 QList<QString> removed;
207 std::set_difference(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(),
208 std::inserter(removed, removed.begin()));
209
210 auto platformScreenByName = [](const QString &name, QList<QOffscreenScreen *> screens) -> QOffscreenScreen * {
212 if (screen->m_name == name)
213 return screen;
214 }
215 Q_UNREACHABLE();
216 };
217
218 auto screenConfigByName = [](const QString &name, QJsonArray screenConfigs) -> QJsonValue {
219 for (QJsonValue screenConfig : screenConfigs) {
220 if (screenConfig["name"].toString() == name)
221 return screenConfig;
222 }
223 Q_UNREACHABLE();
224 };
225
226 auto geometryFromConfig = [](const QJsonObject &config) -> QRect {
227 return QRect(config["x"].toInt(0), config["y"].toInt(0), config["width"].toInt(640), config["height"].toInt(480));
228 };
229
230 // Remove removed screens
231 for (const QString &remove : removed) {
232 QOffscreenScreen *screen = platformScreenByName(remove, m_screens);
235 }
236
237 // Add new screens
238 for (const QString &add : added) {
239 QJsonValue configValue = screenConfigByName(add, newScreens);
240 QJsonObject config = configValue.toObject();
241 if (config.isEmpty()) {
242 qWarning("empty screen object");
243 continue;
244 }
245 QOffscreenScreen *offscreenScreen = new QOffscreenScreen(this);
246 offscreenScreen->m_name = config["name"].toString();
247 offscreenScreen->m_geometry = geometryFromConfig(config);
248 offscreenScreen->m_logicalDpi = config["logicalDpi"].toInt(96);
249 offscreenScreen->m_logicalBaseDpi = config["logicalBaseDpi"].toInt(96);
250 offscreenScreen->m_dpr = config["dpr"].toDouble(1.0);
251 m_screens.append(offscreenScreen);
253 }
254
255 // Update present screens
256 for (const QString &pres : present) {
257 QOffscreenScreen *screen = platformScreenByName(pres, m_screens);
259 QJsonObject currentConfig = screenConfigByName(pres, currentScreens).toObject();
260 QJsonObject newConfig = screenConfigByName(pres, newScreens).toObject();
261
262 // Name can't change, because it'd be a different screen
263 Q_ASSERT(currentConfig["name"] == newConfig["name"]);
264
265 // Geometry
266 QRect currentGeomtry = geometryFromConfig(currentConfig);
267 QRect newGeomtry = geometryFromConfig(newConfig);
268 if (currentGeomtry != newGeomtry) {
269 screen->m_geometry = newGeomtry;
270 QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), newGeomtry, newGeomtry);
271 }
272
273 // logical DPI
274 int currentLogicalDpi = currentConfig["logicalDpi"].toInt(96);
275 int newLogicalDpi = newConfig["logicalDpi"].toInt(96);
276 if (currentLogicalDpi != newLogicalDpi) {
277 screen->m_logicalDpi = newLogicalDpi;
278 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), newLogicalDpi, newLogicalDpi);
279 }
280
281 // The base DPI is more of a platform constant, and should not change, and
282 // there is no handleChange function for it. Print a warning.
283 int currentLogicalBaseDpi = currentConfig["logicalBaseDpi"].toInt(96);
284 int newLogicalBaseDpi = newConfig["logicalBaseDpi"].toInt(96);
285 if (currentLogicalBaseDpi != newLogicalBaseDpi) {
286 screen->m_logicalBaseDpi = newLogicalBaseDpi;
287 qWarning("You ain't supposed to change logicalBaseDpi - its a platform constant. Qt may not react to the change");
288 }
289
290 // DPR. There is also no handleChange function in Qt at this point, instead
291 // the new DPR value will be used during the next repaint. We could repaint
292 // all windows here, but don't. Print a warning.
293 double currentDpr = currentConfig["dpr"].toDouble(1);
294 double newDpr = newConfig["dpr"].toDouble(1);
295 if (currentDpr != newDpr) {
296 screen->m_dpr = newDpr;
297 qWarning("DPR change notifications is not implemented - Qt may not react to the change");
298 }
299 }
300
301 // Now the new configuration is the current configuration
303}
304
309
314
319
321{
322 switch (cap) {
323 case ThreadedPixmaps: return true;
324 case MultipleWindows: return true;
325 case RhiBasedRendering: return false;
327 }
328}
329
337
342
344{
345#if defined(Q_OS_UNIX)
346 return createUnixEventDispatcher();
347#elif defined(Q_OS_WIN)
348 return new QOffscreenEventDispatcher<QEventDispatcherWin32>();
349#else
350 return 0;
351#endif
352}
353
360
361static QString themeName() { return QStringLiteral("offscreen"); }
362
367
368// Restrict the styles to "fusion" to prevent native styles requiring native
369// window handles (eg Windows Vista style) from being used.
371{
372public:
374
376 {
377 switch (h) {
378 case StyleNames:
379 return QVariant(QStringList(QStringLiteral("Fusion")));
380 default:
381 break;
382 }
384 }
385
386 virtual const QFont *font(Font type = SystemFont) const override
387 {
388 static QFont systemFont("Sans Serif"_L1, 9);
389 static QFont fixedFont("monospace"_L1, 9);
390 switch (type) {
392 return &systemFont;
394 return &fixedFont;
395 default:
396 return nullptr;
397 }
398 }
399};
400
402{
403 return name == themeName() ? new OffscreenTheme() : nullptr;
404}
405
410
411#if QT_CONFIG(draganddrop)
412QPlatformDrag *QOffscreenIntegration::drag() const
413{
414 return m_drag.data();
415}
416#endif
417
422
424{
425 QOffscreenIntegration *offscreenIntegration = nullptr;
426
427#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
428 QByteArray glx = qgetenv("QT_QPA_OFFSCREEN_NO_GLX");
429 if (glx.isEmpty())
430 offscreenIntegration = new QOffscreenX11Integration(paramList);
431#endif
432
433 if (!offscreenIntegration)
434 offscreenIntegration = new QOffscreenIntegration(paramList);
435 return offscreenIntegration;
436}
437
438QList<QOffscreenScreen *> QOffscreenIntegration::screens() const
439{
440 return m_screens;
441}
442
QVariant themeHint(ThemeHint h) const override
virtual const QFont * font(Font type=SystemFont) const override
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition qfile.h:93
\reentrant
Definition qfont.h:22
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
\inmodule QtCore\reentrant
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
\inmodule QtCore\reentrant
Definition qjsonvalue.h:25
QJsonObject toObject() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const noexcept
Definition qlist.h:401
value_type takeLast()
Definition qlist.h:567
qsizetype removeAll(const AT &t)
Definition qlist.h:592
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qobject.h:103
QOffscreenEventDispatcher(QObject *parent=nullptr)
bool processEvents(QEventLoop::ProcessEventsFlags flags) override
QList< QOffscreenScreen * > m_screens
QPlatformFontDatabase * fontDatabase() const override
Accessor for the platform integration's fontdatabase.
QScopedPointer< QPlatformNativeInterface > m_nativeInterface
QScopedPointer< QPlatformInputContext > m_inputContext
std::optional< QJsonObject > resolveConfigFileConfiguration(const QStringList &paramList) const
void initialize() override
Performs initialization steps that depend on having an event dispatcher available.
QList< QOffscreenScreen * > screens() const
QPlatformWindow * createPlatformWindow(QWindow *window) const override
Factory function for QPlatformWindow.
QJsonObject configuration() const
QPlatformBackingStore * createPlatformBackingStore(QWindow *window) const override
Factory function for QPlatformBackingStore.
QPlatformTheme * createPlatformTheme(const QString &name) const override
QPlatformInputContext * inputContext() const override
Returns the platforms input context.
QOffscreenIntegration(const QStringList &paramList)
QScopedPointer< QPlatformFontDatabase > m_fontDatabase
QPlatformServices * services() const override
QPlatformNativeInterface * nativeInterface() const override
static QOffscreenIntegration * createOffscreenIntegration(const QStringList &paramList)
bool hasCapability(QPlatformIntegration::Capability cap) const override
QJsonObject defaultConfiguration() const
QStringList themeNames() const override
void setConfiguration(const QJsonObject &configuration)
QScopedPointer< QPlatformServices > m_services
QAbstractEventDispatcher * createEventDispatcher() const override
Factory function for the GUI event dispatcher.
The QPlatformBackingStore class provides the drawing area for top-level windows.
The QPlatformDrag class provides an abstraction for drag.
The QPlatformFontDatabase class makes it possible to customize how fonts are discovered and how they ...
static QPlatformInputContext * create()
The QPlatformInputContext class abstracts the input method dependent data and composing state.
virtual bool hasCapability(Capability cap) const
Capability
Capabilities are used to determine specific features of a platform integration.
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
The QPlatformServices provides the backend for desktop-related functionality.
The QPlatformTheme class allows customizing the UI based on themes.
virtual QVariant themeHint(ThemeHint hint) const
ThemeHint
This enum describes the available theme hints.
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qrect.h:30
T * get() const noexcept
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
Definition qvariant.h:65
static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry, const QRect &newAvailableGeometry)
static bool sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
static void setSynchronousWindowSystemEvents(bool enable)
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary=false)
Should be called by the implementation whenever a new screen is added.
static void handleScreenRemoved(QPlatformScreen *screen)
Should be called by the implementation whenever a screen is removed.
static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY)
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError * error
EGLConfig config
QPlatformFontDatabase QGenericUnixFontDatabase
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
static QString themeName()
GLfloat GLfloat GLfloat w
[0]
GLenum type
GLbitfield flags
GLenum const GLint * param
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLuint GLuint * names
GLenum cap
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
static int defaultScreen
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
#define QStringLiteral(str)
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
static int toInt(const QChar &qc, int R)
settings remove("monkey")
aWidget window() -> setWindowTitle("New Window Title")
[2]
char * toString(const MyType &t)
[31]
\inmodule QtCore\reentrant
QT_BEGIN_NAMESPACE bool toBool(const QString &str)
Definition utils.h:14