1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qtdiag.h"
5
6#include <QtGui/QGuiApplication>
7#include <QtGui/QStyleHints>
8#include <QtGui/QScreen>
9#include <QtGui/QFont>
10#include <QtGui/QFontDatabase>
11#include <QtGui/QPalette>
12#ifndef QT_NO_OPENGL
13# include <QtGui/QOpenGLContext>
14# include <QtGui/QOpenGLFunctions>
15# include <QtOpenGL/QOpenGLVersionProfile>
16# include <QtOpenGL/QOpenGLVersionFunctions>
17# include <QtOpenGL/QOpenGLVersionFunctionsFactory>
18#endif // QT_NO_OPENGL
19#if QT_CONFIG(vulkan)
20# include <QtGui/QVulkanInstance>
21# include <QtGui/QVulkanWindow>
22#endif // vulkan
23#include <QtGui/QWindow>
24#include <QtGui/QInputDevice>
25
26#ifdef NETWORK_DIAG
27# include <QSslSocket>
28#endif
29
30#include <QtCore/QLibraryInfo>
31#include <QtCore/QStringList>
32#include <QtCore/QVariant>
33#include <QtCore/QSysInfo>
34#include <QtCore/QLibraryInfo>
35#if QT_CONFIG(processenvironment)
36# include <QtCore/QProcessEnvironment>
37#endif
38#include <QtCore/QTextStream>
39#include <QtCore/QStandardPaths>
40#include <QtCore/QDir>
41#include <QtCore/QFileSelector>
42#include <QtCore/QDebug>
43#include <QtCore/QVersionNumber>
44
45#include <private/qsimd_p.h>
46#include <private/qguiapplication_p.h>
47#include <qpa/qplatformintegration.h>
48#include <qpa/qplatformscreen.h>
49#include <qpa/qplatformtheme.h>
50#include <qpa/qplatformthemefactory_p.h>
51#include <qpa/qplatformintegration.h>
52#include <private/qhighdpiscaling_p.h>
53
54#include <QtGui/QOffscreenSurface>
55#include <rhi/qrhi.h>
56
57#ifdef QT_WIDGETS_LIB
58# include <QtWidgets/QStyleFactory>
59#endif
60
61#include <algorithm>
62
63QT_BEGIN_NAMESPACE
64
65QTextStream &operator<<(QTextStream &str, const QSize &s)
66{
67 str << s.width() << 'x' << s.height();
68 return str;
69}
70
71QTextStream &operator<<(QTextStream &str, const QSizeF &s)
72{
73 str << s.width() << 'x' << s.height();
74 return str;
75}
76
77QTextStream &operator<<(QTextStream &str, const QDpi &d)
78{
79 str << d.first << ',' << d.second;
80 return str;
81}
82
83QTextStream &operator<<(QTextStream &str, const QRect &r)
84{
85 str << r.size() << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
86 return str;
87}
88
89QTextStream &operator<<(QTextStream &str, const QStringList &l)
90{
91 for (int i = 0; i < l.size(); ++i) {
92 if (i)
93 str << ',';
94 str << l.at(i);
95 }
96 return str;
97}
98
99QTextStream &operator<<(QTextStream &str, const QFont &f)
100{
101 str << '"' << f.family() << "\" " << f.pointSize();
102 return str;
103}
104
105QTextStream &operator<<(QTextStream &str, QPlatformScreen::SubpixelAntialiasingType st)
106{
107 static const char *enumValues[] = {
108 "Subpixel_None", "Subpixel_RGB", "Subpixel_BGR", "Subpixel_VRGB", "Subpixel_VBGR"
109 };
110 str << (size_t(st) < sizeof(enumValues) / sizeof(enumValues[0])
111 ? enumValues[st] : "<Unknown>");
112 return str;
113}
114
115QTextStream &operator<<(QTextStream &str, const QRhiDriverInfo &info)
116{
117 static const char *enumValues[] = {
118 "Unknown", "Integrated", "Discrete", "External", "Virtual", "Cpu"
119 };
120 str << "Device: " << info.deviceName
121 << " Device ID: 0x" << Qt::hex << info.deviceId
122 << " Vendor ID: 0x" << info.vendorId << Qt::dec
123 << " Device type: " << (size_t(info.deviceType) < sizeof(enumValues) / sizeof(enumValues[0])
124 ? enumValues[info.deviceType] : "<Unknown>");
125 return str;
126}
127
128#ifndef QT_NO_OPENGL
129
130QTextStream &operator<<(QTextStream &str, const QSurfaceFormat &format)
131{
132 str << "Version: " << format.majorVersion() << '.'
133 << format.minorVersion() << " Profile: " << format.profile()
134 << " Swap behavior: " << format.swapBehavior()
135 << " Buffer size (RGB";
136 if (format.hasAlpha())
137 str << 'A';
138 str << "): " << format.redBufferSize() << ',' << format.greenBufferSize()
139 << ',' << format.blueBufferSize();
140 if (format.hasAlpha())
141 str << ',' << format.alphaBufferSize();
142 if (const int dbs = format.depthBufferSize())
143 str << " Depth buffer: " << dbs;
144 if (const int sbs = format.stencilBufferSize())
145 str << " Stencil buffer: " << sbs;
146 const int samples = format.samples();
147 if (samples > 0)
148 str << " Samples: " << samples;
149 return str;
150}
151
152void dumpGlInfo(QTextStream &str, bool listExtensions)
153{
154 QOpenGLContext context;
155 if (context.create()) {
156# ifdef QT_OPENGL_DYNAMIC
157 str << "Dynamic GL ";
158# endif
159 switch (context.openGLModuleType()) {
160 case QOpenGLContext::LibGL:
161 str << "LibGL";
162 break;
163 case QOpenGLContext::LibGLES:
164 str << "LibGLES";
165 break;
166 }
167 QWindow window;
168 window.setSurfaceType(QSurface::OpenGLSurface);
169 window.create();
170 context.makeCurrent(surface: &window);
171 QOpenGLFunctions functions(&context);
172
173 str << " Vendor: " << reinterpret_cast<const char *>(functions.glGetString(GL_VENDOR))
174 << "\nRenderer: " << reinterpret_cast<const char *>(functions.glGetString(GL_RENDERER))
175 << "\nVersion: " << reinterpret_cast<const char *>(functions.glGetString(GL_VERSION))
176 << "\nShading language: " << reinterpret_cast<const char *>(functions.glGetString(GL_SHADING_LANGUAGE_VERSION))
177 << "\nFormat: " << context.format();
178# if !QT_CONFIG(opengles2)
179 GLint majorVersion;
180 functions.glGetIntegerv(GL_MAJOR_VERSION, params: &majorVersion);
181 GLint minorVersion;
182 functions.glGetIntegerv(GL_MINOR_VERSION, params: &minorVersion);
183 const QByteArray openGlVersionFunctionsName = "QOpenGLFunctions_"
184 + QByteArray::number(majorVersion) + '_' + QByteArray::number(minorVersion);
185 str << "\nProfile: None (" << openGlVersionFunctionsName << ')';
186 if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) {
187 QOpenGLVersionProfile profile;
188 profile.setVersion(majorVersion, minorVersion);
189 profile.setProfile(QSurfaceFormat::CoreProfile);
190 if (auto f = QOpenGLVersionFunctionsFactory::get(versionProfile: profile, context: &context)) {
191 if (f->initializeOpenGLFunctions())
192 str << ", Core (" << openGlVersionFunctionsName << "_Core)";
193 }
194 profile.setProfile(QSurfaceFormat::CompatibilityProfile);
195 if (auto f = QOpenGLVersionFunctionsFactory::get(versionProfile: profile, context: &context)) {
196 if (f->initializeOpenGLFunctions())
197 str << ", Compatibility (" << openGlVersionFunctionsName << "_Compatibility)";
198 }
199 }
200 str << '\n';
201# endif // !QT_CONFIG(opengles2)
202 if (listExtensions) {
203 QByteArrayList extensionList = context.extensions().values();
204 std::sort(first: extensionList.begin(), last: extensionList.end());
205 str << " \nFound " << extensionList.size() << " extensions:\n";
206 for (const QByteArray &extension : std::as_const(t&: extensionList))
207 str << " " << extension << '\n';
208 }
209 } else {
210 str << "Unable to create an Open GL context.\n";
211 }
212}
213
214#endif // !QT_NO_OPENGL
215
216#if QT_CONFIG(vulkan)
217QVersionNumber vulkanVersion(uint32_t v)
218{
219 return QVersionNumber(VK_VERSION_MAJOR(v), VK_VERSION_MINOR(v), VK_VERSION_PATCH(v));
220}
221
222void dumpVkInfo(QTextStream &str)
223{
224 QVulkanInstance inst;
225 if (inst.create()) {
226 str << "Vulkan instance available\n";
227 str << "Supported instance extensions:\n";
228 for (const QVulkanExtension &ext : inst.supportedExtensions())
229 str << " " << ext.name << ", version " << ext.version << "\n";
230 str << "Supported layers:\n";
231 for (const QVulkanLayer &layer : inst.supportedLayers())
232 str << " " << layer.name << ", version " << layer.version
233 << ", spec version " << layer.specVersion.toString()
234 << ", " << layer.description << "\n";
235 // Show at least the available physical devices. Anything additional
236 // needs lots of initialization, or, if done through QVulkanWindow, an
237 // exposed window. None of these are very tempting right now.
238 str << "Available physical devices:\n";
239 QVulkanWindow window;
240 window.setVulkanInstance(&inst);
241 for (const VkPhysicalDeviceProperties &props : window.availablePhysicalDevices()) {
242 str << " API version " << vulkanVersion(v: props.apiVersion).toString()
243 << Qt::hex << ", vendor 0x" << props.vendorID
244 << ", device 0x" << props.deviceID << ", " << props.deviceName
245 << Qt::dec << ", type " << props.deviceType
246 << ", driver version " << vulkanVersion(v: props.driverVersion).toString();
247 }
248 } else {
249 str << "Unable to create a Vulkan instance, error code is" << inst.errorCode() << "\n";
250 }
251}
252#endif // vulkan
253
254void dumpRhiBackendInfo(QTextStream &str, const char *name, QRhi::Implementation impl, QRhiInitParams *initParams)
255{
256 struct RhiFeature {
257 const char *name;
258 QRhi::Feature val;
259 };
260 const RhiFeature features[] = {
261 { .name: "MultisampleTexture", .val: QRhi::MultisampleTexture },
262 { .name: "MultisampleRenderBuffer", .val: QRhi::MultisampleRenderBuffer },
263 { .name: "DebugMarkers", .val: QRhi::DebugMarkers },
264 { .name: "Timestamps", .val: QRhi::Timestamps },
265 { .name: "Instancing", .val: QRhi::Instancing },
266 { .name: "CustomInstanceStepRate", .val: QRhi::CustomInstanceStepRate },
267 { .name: "PrimitiveRestart", .val: QRhi::PrimitiveRestart },
268 { .name: "NonDynamicUniformBuffers", .val: QRhi::NonDynamicUniformBuffers },
269 { .name: "NonFourAlignedEffectiveIndexBufferOffset", .val: QRhi::NonFourAlignedEffectiveIndexBufferOffset },
270 { .name: "NPOTTextureRepeat", .val: QRhi::NPOTTextureRepeat },
271 { .name: "RedOrAlpha8IsRed", .val: QRhi::RedOrAlpha8IsRed },
272 { .name: "ElementIndexUint", .val: QRhi::ElementIndexUint },
273 { .name: "Compute", .val: QRhi::Compute },
274 { .name: "WideLines", .val: QRhi::WideLines },
275 { .name: "VertexShaderPointSize", .val: QRhi::VertexShaderPointSize },
276 { .name: "BaseVertex", .val: QRhi::BaseVertex },
277 { .name: "BaseInstance", .val: QRhi::BaseInstance },
278 { .name: "TriangleFanTopology", .val: QRhi::TriangleFanTopology },
279 { .name: "ReadBackNonUniformBuffer", .val: QRhi::ReadBackNonUniformBuffer },
280 { .name: "ReadBackNonBaseMipLevel", .val: QRhi::ReadBackNonBaseMipLevel },
281 { .name: "TexelFetch", .val: QRhi::TexelFetch },
282 { .name: "RenderToNonBaseMipLevel", .val: QRhi::RenderToNonBaseMipLevel },
283 { .name: "IntAttributes", .val: QRhi::IntAttributes },
284 { .name: "ScreenSpaceDerivatives", .val: QRhi::ScreenSpaceDerivatives },
285 { .name: "ReadBackAnyTextureFormat", .val: QRhi::ReadBackAnyTextureFormat },
286 { .name: "PipelineCacheDataLoadSave", .val: QRhi::PipelineCacheDataLoadSave },
287 { .name: "ImageDataStride", .val: QRhi::ImageDataStride },
288 { .name: "RenderBufferImport", .val: QRhi::RenderBufferImport },
289 { .name: "ThreeDimensionalTextures", .val: QRhi::ThreeDimensionalTextures },
290 { .name: "RenderTo3DTextureSlice", .val: QRhi::RenderTo3DTextureSlice },
291 { .name: "TextureArrays", .val: QRhi::TextureArrays },
292 { .name: "Tessellation", .val: QRhi::Tessellation },
293 { .name: "GeometryShader", .val: QRhi::GeometryShader },
294 { .name: "TextureArrayRange", .val: QRhi::TextureArrayRange },
295 { .name: "NonFillPolygonMode", .val: QRhi::NonFillPolygonMode },
296 { .name: "OneDimensionalTextures", .val: QRhi::OneDimensionalTextures },
297 { .name: "OneDimensionalTextureMipmaps", .val: QRhi::OneDimensionalTextureMipmaps },
298 { .name: "HalfAttributes", .val: QRhi::HalfAttributes },
299 { .name: "RenderToOneDimensionalTexture", .val: QRhi::RenderToOneDimensionalTexture },
300 { .name: "ThreeDimensionalTextureMipmaps", .val: QRhi::ThreeDimensionalTextureMipmaps },
301
302 { .name: nullptr, .val: QRhi::Feature(0) }
303 };
304 struct RhiTextureFormat {
305 const char *name;
306 QRhiTexture::Format val;
307 };
308 const RhiTextureFormat textureFormats[] = {
309 { .name: "RGBA8", .val: QRhiTexture::RGBA8 },
310 { .name: "BGRA8", .val: QRhiTexture::BGRA8 },
311 { .name: "R8", .val: QRhiTexture::R8 },
312 { .name: "RG8", .val: QRhiTexture::RG8 },
313 { .name: "R16", .val: QRhiTexture::R16 },
314 { .name: "RG16", .val: QRhiTexture::RG16 },
315 { .name: "RED_OR_ALPHA8", .val: QRhiTexture::RED_OR_ALPHA8 },
316 { .name: "RGBA16F", .val: QRhiTexture::RGBA16F },
317 { .name: "RGBA32F", .val: QRhiTexture::RGBA32F },
318 { .name: "R16F", .val: QRhiTexture::R16F },
319 { .name: "R32F", .val: QRhiTexture::R32F },
320 { .name: "RGB10A2", .val: QRhiTexture::RGB10A2 },
321 { .name: "D16", .val: QRhiTexture::D16 },
322 { .name: "D24", .val: QRhiTexture::D24 },
323 { .name: "D24S8", .val: QRhiTexture::D24S8 },
324 { .name: "D32F", .val: QRhiTexture::D32F },
325 { .name: "BC1", .val: QRhiTexture::BC1 },
326 { .name: "BC2", .val: QRhiTexture::BC2 },
327 { .name: "BC3", .val: QRhiTexture::BC3 },
328 { .name: "BC4", .val: QRhiTexture::BC4 },
329 { .name: "BC5", .val: QRhiTexture::BC5 },
330 { .name: "BC6H", .val: QRhiTexture::BC6H },
331 { .name: "BC7", .val: QRhiTexture::BC7 },
332 { .name: "ETC2_RGB8", .val: QRhiTexture::ETC2_RGB8 },
333 { .name: "ETC2_RGB8A1", .val: QRhiTexture::ETC2_RGB8A1 },
334 { .name: "ETC2_RGBA8", .val: QRhiTexture::ETC2_RGBA8 },
335 { .name: "ASTC_4x4", .val: QRhiTexture::ASTC_4x4 },
336 { .name: "ASTC_5x4", .val: QRhiTexture::ASTC_5x4 },
337 { .name: "ASTC_5x5", .val: QRhiTexture::ASTC_5x5 },
338 { .name: "ASTC_6x5", .val: QRhiTexture::ASTC_6x5 },
339 { .name: "ASTC_6x6", .val: QRhiTexture::ASTC_6x6 },
340 { .name: "ASTC_8x5", .val: QRhiTexture::ASTC_8x5 },
341 { .name: "ASTC_8x6", .val: QRhiTexture::ASTC_8x6 },
342 { .name: "ASTC_8x8", .val: QRhiTexture::ASTC_8x8 },
343 { .name: "ASTC_10x5", .val: QRhiTexture::ASTC_10x5 },
344 { .name: "ASTC_10x6", .val: QRhiTexture::ASTC_10x6 },
345 { .name: "ASTC_10x8", .val: QRhiTexture::ASTC_10x8 },
346 { .name: "ASTC_10x10", .val: QRhiTexture::ASTC_10x10 },
347 { .name: "ASTC_12x10", .val: QRhiTexture::ASTC_12x10 },
348 { .name: "ASTC_12x12", .val: QRhiTexture::ASTC_12x12 },
349 { .name: nullptr, .val: QRhiTexture::UnknownFormat }
350 };
351
352 QScopedPointer<QRhi> rhi(QRhi::create(impl, params: initParams, flags: QRhi::Flags(), importDevice: nullptr));
353 if (rhi) {
354 str << name << ":\n";
355 str << " Driver Info: " << rhi->driverInfo() << "\n";
356 str << " Min Texture Size: " << rhi->resourceLimit(limit: QRhi::TextureSizeMin) << "\n";
357 str << " Max Texture Size: " << rhi->resourceLimit(limit: QRhi::TextureSizeMax) << "\n";
358 str << " Max Color Attachments: " << rhi->resourceLimit(limit: QRhi::MaxColorAttachments) << "\n";
359 str << " Frames in Flight: " << rhi->resourceLimit(limit: QRhi::FramesInFlight) << "\n";
360 str << " Async Readback Limit: " << rhi->resourceLimit(limit: QRhi::MaxAsyncReadbackFrames) << "\n";
361 str << " MaxThreadGroupsPerDimension: " << rhi->resourceLimit(limit: QRhi::MaxThreadGroupsPerDimension) << "\n";
362 str << " MaxThreadsPerThreadGroup: " << rhi->resourceLimit(limit: QRhi::MaxThreadsPerThreadGroup) << "\n";
363 str << " MaxThreadGroupX: " << rhi->resourceLimit(limit: QRhi::MaxThreadGroupX) << "\n";
364 str << " MaxThreadGroupY: " << rhi->resourceLimit(limit: QRhi::MaxThreadGroupY) << "\n";
365 str << " MaxThreadGroupZ: " << rhi->resourceLimit(limit: QRhi::MaxThreadGroupZ) << "\n";
366 str << " TextureArraySizeMax: " << rhi->resourceLimit(limit: QRhi::TextureArraySizeMax) << "\n";
367 str << " MaxUniformBufferRange: " << rhi->resourceLimit(limit: QRhi::MaxUniformBufferRange) << "\n";
368 str << " MaxVertexInputs: " << rhi->resourceLimit(limit: QRhi::MaxVertexInputs) << "\n";
369 str << " MaxVertexOutputs: " << rhi->resourceLimit(limit: QRhi::MaxVertexOutputs) << "\n";
370 str << " Uniform Buffer Alignment: " << rhi->ubufAlignment() << "\n";
371 QByteArrayList supportedSampleCounts;
372 for (int s : rhi->supportedSampleCounts())
373 supportedSampleCounts << QByteArray::number(s);
374 str << " Supported MSAA sample counts: " << supportedSampleCounts.join(sep: ',') << "\n";
375 str << " Features:\n";
376 for (int i = 0; features[i].name; i++) {
377 str << " " << (rhi->isFeatureSupported(feature: features[i].val) ? "v" : "-") << " " << features[i].name << "\n";
378 }
379 str << " Texture formats:";
380 for (int i = 0; textureFormats[i].name; i++) {
381 if (rhi->isTextureFormatSupported(format: textureFormats[i].val))
382 str << " " << textureFormats[i].name;
383 }
384 str << "\n";
385 }
386}
387
388void dumpRhiInfo(QTextStream &str)
389{
390 str << "Qt Rendering Hardware Interface supported backends:\n";
391
392#if QT_CONFIG(opengl)
393 {
394 QRhiGles2InitParams params;
395 params.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
396 dumpRhiBackendInfo(str, name: "OpenGL (with default QSurfaceFormat)", impl: QRhi::OpenGLES2, initParams: &params);
397 delete params.fallbackSurface;
398 }
399#endif
400
401#if QT_CONFIG(vulkan)
402 {
403 QVulkanInstance vulkanInstance;
404 vulkanInstance.create();
405 QRhiVulkanInitParams params;
406 params.inst = &vulkanInstance;
407 dumpRhiBackendInfo(str, name: "Vulkan", impl: QRhi::Vulkan, initParams: &params);
408 vulkanInstance.destroy();
409 }
410#endif
411
412#ifdef Q_OS_WIN
413 {
414 QRhiD3D11InitParams params;
415 dumpRhiBackendInfo(str, "Direct3D 11", QRhi::D3D11, &params);
416 }
417 {
418 QRhiD3D12InitParams params;
419 dumpRhiBackendInfo(str, "Direct3D 12", QRhi::D3D12, &params);
420 }
421#endif
422
423#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
424 {
425 QRhiMetalInitParams params;
426 dumpRhiBackendInfo(str, "Metal", QRhi::Metal, &params);
427 }
428#endif
429}
430
431#define DUMP_CAPABILITY(str, integration, capability) \
432 if (platformIntegration->hasCapability(QPlatformIntegration::capability)) \
433 str << ' ' << #capability;
434
435// Dump values of QStandardPaths, indicate writable locations by asterisk.
436static void dumpStandardLocation(QTextStream &str, QStandardPaths::StandardLocation location)
437{
438 str << '"' << QStandardPaths::displayName(type: location) << '"';
439 const QStringList directories = QStandardPaths::standardLocations(type: location);
440 const QString writableDirectory = QStandardPaths::writableLocation(type: location);
441 const int writableIndex = writableDirectory.isEmpty() ? -1 : directories.indexOf(str: writableDirectory);
442 for (int i = 0; i < directories.size(); ++i) {
443 str << ' ';
444 if (i == writableIndex)
445 str << '*';
446 str << QDir::toNativeSeparators(pathName: directories.at(i));
447 if (i == writableIndex)
448 str << '*';
449 }
450 if (!writableDirectory.isEmpty() && writableIndex < 0)
451 str << " *" << QDir::toNativeSeparators(pathName: writableDirectory) << '*';
452}
453
454#define DUMP_CPU_FEATURE(feature, name) \
455 if (qCpuHasFeature(feature)) \
456 str << " " name
457
458#define DUMP_STANDARDPATH(str, location) \
459 str << " " << #location << ": "; \
460 dumpStandardLocation(str, QStandardPaths::location); \
461 str << '\n';
462
463#define DUMP_LIBRARYPATH(str, loc) \
464 str << " " << #loc << ": " << QDir::toNativeSeparators(QLibraryInfo::path(QLibraryInfo::loc)) << '\n';
465
466// Helper to format a type via QDebug to be used for QFlags/Q_ENUM.
467template <class T>
468static QString formatQDebug(T t)
469{
470 QString result;
471 QDebug(&result) << t;
472 return result;
473}
474
475// Helper to format a type via QDebug, stripping the class name.
476template <class T>
477static QString formatValueQDebug(T t)
478{
479 QString result = formatQDebug(t).trimmed();
480 if (result.endsWith(c: QLatin1Char(')'))) {
481 result.chop(n: 1);
482 result.remove(i: 0, len: result.indexOf(c: QLatin1Char('(')) + 1);
483 }
484 return result;
485}
486
487QTextStream &operator<<(QTextStream &str, const QPalette &palette)
488{
489 for (int r = 0; r < int(QPalette::NColorRoles); ++r) {
490 const QPalette::ColorRole role = static_cast< QPalette::ColorRole>(r);
491 const QColor color = palette.color(cg: QPalette::Active, cr: role);
492 if (color.isValid())
493 str << " " << formatValueQDebug(t: role) << ": " << color.name(format: QColor::HexArgb) << '\n';
494 }
495 return str;
496}
497
498static inline QByteArrayList qtFeatures()
499{
500 QByteArrayList result;
501#ifdef QT_NO_CLIPBOARD
502 result.append("QT_NO_CLIPBOARD");
503#endif
504#ifdef QT_NO_CONTEXTMENU
505 result.append("QT_NO_CONTEXTMENU");
506#endif
507#ifdef QT_NO_CURSOR
508 result.append("QT_NO_CURSOR");
509#endif
510#ifdef QT_NO_DRAGANDDROP
511 result.append("QT_NO_DRAGANDDROP");
512#endif
513#ifdef QT_NO_EXCEPTIONS
514 result.append(t: "QT_NO_EXCEPTIONS");
515#endif
516#ifdef QT_NO_LIBRARY
517 result.append("QT_NO_LIBRARY");
518#endif
519#ifdef QT_NO_NETWORK
520 result.append("QT_NO_NETWORK");
521#endif
522#ifdef QT_NO_OPENGL
523 result.append("QT_NO_OPENGL");
524#endif
525#ifdef QT_NO_OPENSSL
526 result.append("QT_NO_OPENSSL");
527#endif
528#ifdef QT_NO_PROCESS
529 result.append("QT_NO_PROCESS");
530#endif
531#ifdef QT_NO_PRINTER
532 result.append("QT_NO_PRINTER");
533#endif
534#ifdef QT_NO_SESSIONMANAGER
535 result.append("QT_NO_SESSIONMANAGER");
536#endif
537#ifdef QT_NO_SETTINGS
538 result.append("QT_NO_SETTINGS");
539#endif
540#ifdef QT_NO_SHORTCUT
541 result.append("QT_NO_SHORTCUT");
542#endif
543#ifdef QT_NO_SYSTEMTRAYICON
544 result.append("QT_NO_SYSTEMTRAYICON");
545#endif
546#ifdef QT_NO_QTHREAD
547 result.append("QT_NO_QTHREAD");
548#endif
549#ifdef QT_NO_WHATSTHIS
550 result.append("QT_NO_WHATSTHIS");
551#endif
552#ifdef QT_NO_WIDGETS
553 result.append("QT_NO_WIDGETS");
554#endif
555#ifdef QT_NO_ZLIB
556 result.append("QT_NO_ZLIB");
557#endif
558 return result;
559}
560
561QString qtDiag(unsigned flags)
562{
563 QString result;
564 QTextStream str(&result);
565
566 const QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
567 str << QLibraryInfo::build() << " on \"" << QGuiApplication::platformName() << "\" "
568 << '\n'
569 << "OS: " << QSysInfo::prettyProductName()
570 << " [" << QSysInfo::kernelType() << " version " << QSysInfo::kernelVersion() << "]\n";
571
572 str << "\nArchitecture: " << QSysInfo::currentCpuArchitecture() << "; features:";
573#if defined(Q_PROCESSOR_X86)
574 DUMP_CPU_FEATURE(HYBRID, "hybrid");
575 DUMP_CPU_FEATURE(SSE2, "SSE2");
576 DUMP_CPU_FEATURE(SSE3, "SSE3");
577 DUMP_CPU_FEATURE(SSSE3, "SSSE3");
578 DUMP_CPU_FEATURE(SSE4_1, "SSE4.1");
579 DUMP_CPU_FEATURE(SSE4_2, "SSE4.2");
580 DUMP_CPU_FEATURE(AVX, "AVX");
581 DUMP_CPU_FEATURE(AVX2, "AVX2");
582 DUMP_CPU_FEATURE(AVX512F, "AVX512F");
583 DUMP_CPU_FEATURE(AVX512IFMA, "AVX512IFMA");
584 DUMP_CPU_FEATURE(AVX512VBMI2, "AVX512VBMI2");
585 DUMP_CPU_FEATURE(AVX512FP16, "AVX512FP16");
586 DUMP_CPU_FEATURE(RDRND, "RDRAND");
587 DUMP_CPU_FEATURE(RDSEED, "RDSEED");
588 DUMP_CPU_FEATURE(AES, "AES");
589 DUMP_CPU_FEATURE(VAES, "VAES");
590 DUMP_CPU_FEATURE(SHA, "SHA");
591#elif defined(Q_PROCESSOR_ARM)
592 DUMP_CPU_FEATURE(ARM_NEON, "Neon");
593#elif defined(Q_PROCESSOR_MIPS)
594 DUMP_CPU_FEATURE(DSP, "DSP");
595 DUMP_CPU_FEATURE(DSPR2, "DSPR2");
596#endif
597 str << '\n';
598
599#if QT_CONFIG(process)
600 const QProcessEnvironment systemEnvironment = QProcessEnvironment::systemEnvironment();
601 str << "\nEnvironment:\n";
602 const QStringList keys = systemEnvironment.keys();
603 for (const QString &key : keys) {
604 if (key.startsWith(c: QLatin1Char('Q')))
605 str << " " << key << "=\"" << systemEnvironment.value(name: key) << "\"\n";
606 }
607#endif // QT_CONFIG(process)
608
609 const QByteArrayList features = qtFeatures();
610 if (!features.isEmpty())
611 str << "\nFeatures: " << features.join(sep: ' ') << '\n';
612
613 str << "\nLibrary info:\n";
614 DUMP_LIBRARYPATH(str, PrefixPath)
615 DUMP_LIBRARYPATH(str, DocumentationPath)
616 DUMP_LIBRARYPATH(str, HeadersPath)
617 DUMP_LIBRARYPATH(str, LibrariesPath)
618 DUMP_LIBRARYPATH(str, LibraryExecutablesPath)
619 DUMP_LIBRARYPATH(str, BinariesPath)
620 DUMP_LIBRARYPATH(str, PluginsPath)
621 DUMP_LIBRARYPATH(str, QmlImportsPath)
622 DUMP_LIBRARYPATH(str, ArchDataPath)
623 DUMP_LIBRARYPATH(str, DataPath)
624 DUMP_LIBRARYPATH(str, TranslationsPath)
625 DUMP_LIBRARYPATH(str, ExamplesPath)
626 DUMP_LIBRARYPATH(str, TestsPath)
627 DUMP_LIBRARYPATH(str, SettingsPath)
628
629 str << "\nStandard paths [*...* denote writable entry]:\n";
630 DUMP_STANDARDPATH(str, DesktopLocation)
631 DUMP_STANDARDPATH(str, DocumentsLocation)
632 DUMP_STANDARDPATH(str, FontsLocation)
633 DUMP_STANDARDPATH(str, ApplicationsLocation)
634 DUMP_STANDARDPATH(str, MusicLocation)
635 DUMP_STANDARDPATH(str, MoviesLocation)
636 DUMP_STANDARDPATH(str, PicturesLocation)
637 DUMP_STANDARDPATH(str, TempLocation)
638 DUMP_STANDARDPATH(str, HomeLocation)
639 DUMP_STANDARDPATH(str, AppLocalDataLocation)
640 DUMP_STANDARDPATH(str, CacheLocation)
641 DUMP_STANDARDPATH(str, GenericDataLocation)
642 DUMP_STANDARDPATH(str, RuntimeLocation)
643 DUMP_STANDARDPATH(str, ConfigLocation)
644 DUMP_STANDARDPATH(str, DownloadLocation)
645 DUMP_STANDARDPATH(str, GenericCacheLocation)
646 DUMP_STANDARDPATH(str, GenericConfigLocation)
647 DUMP_STANDARDPATH(str, AppDataLocation)
648 DUMP_STANDARDPATH(str, AppConfigLocation)
649
650 str << "\nFile selectors (increasing order of precedence):\n ";
651 const QStringList allSelectors = QFileSelector().allSelectors();
652 for (const QString &s : allSelectors)
653 str << ' ' << s;
654
655 str << "\n\nNetwork:\n ";
656#ifdef NETWORK_DIAG
657# ifndef QT_NO_SSL
658 if (QSslSocket::supportsSsl()) {
659 str << "Using \"" << QSslSocket::sslLibraryVersionString() << "\", version: 0x"
660 << Qt::hex << QSslSocket::sslLibraryVersionNumber() << Qt::dec;
661 } else {
662 str << "\nSSL is not supported.";
663 }
664# else // !QT_NO_SSL
665 str << "SSL is not available.";
666# endif // QT_NO_SSL
667#else
668 str << "Qt Network module is not available.";
669#endif // NETWORK_DIAG
670
671 str << "\n\nPlatform capabilities:";
672 DUMP_CAPABILITY(str, platformIntegration, ThreadedPixmaps)
673 DUMP_CAPABILITY(str, platformIntegration, OpenGL)
674 DUMP_CAPABILITY(str, platformIntegration, ThreadedOpenGL)
675 DUMP_CAPABILITY(str, platformIntegration, SharedGraphicsCache)
676 DUMP_CAPABILITY(str, platformIntegration, BufferQueueingOpenGL)
677 DUMP_CAPABILITY(str, platformIntegration, WindowMasks)
678 DUMP_CAPABILITY(str, platformIntegration, MultipleWindows)
679 DUMP_CAPABILITY(str, platformIntegration, ApplicationState)
680 DUMP_CAPABILITY(str, platformIntegration, ForeignWindows)
681 DUMP_CAPABILITY(str, platformIntegration, NonFullScreenWindows)
682 DUMP_CAPABILITY(str, platformIntegration, NativeWidgets)
683 DUMP_CAPABILITY(str, platformIntegration, WindowManagement)
684 DUMP_CAPABILITY(str, platformIntegration, SyncState)
685 DUMP_CAPABILITY(str, platformIntegration, RasterGLSurface)
686 DUMP_CAPABILITY(str, platformIntegration, AllGLFunctionsQueryable)
687 DUMP_CAPABILITY(str, platformIntegration, ApplicationIcon)
688 DUMP_CAPABILITY(str, platformIntegration, SwitchableWidgetComposition)
689 str << '\n';
690
691 const QStyleHints *styleHints = QGuiApplication::styleHints();
692 const QChar passwordMaskCharacter = styleHints->passwordMaskCharacter();
693 str << "\nStyle hints:\n mouseDoubleClickInterval: " << styleHints->mouseDoubleClickInterval() << '\n'
694 << " mousePressAndHoldInterval: " << styleHints->mousePressAndHoldInterval() << '\n'
695 << " startDragDistance: " << styleHints->startDragDistance() << '\n'
696 << " startDragTime: " << styleHints->startDragTime() << '\n'
697 << " startDragVelocity: " << styleHints->startDragVelocity() << '\n'
698 << " keyboardInputInterval: " << styleHints->keyboardInputInterval() << '\n'
699 << " keyboardAutoRepeatRateF: " << styleHints->keyboardAutoRepeatRateF() << '\n'
700 << " cursorFlashTime: " << styleHints->cursorFlashTime() << '\n'
701 << " showIsFullScreen: " << styleHints->showIsFullScreen() << '\n'
702 << " showIsMaximized: " << styleHints->showIsMaximized() << '\n'
703 << " passwordMaskDelay: " << styleHints->passwordMaskDelay() << '\n'
704 << " passwordMaskCharacter: ";
705 const int passwordMaskCharacterUc = passwordMaskCharacter.unicode();
706 if (passwordMaskCharacterUc >= 32 && passwordMaskCharacterUc < 128) {
707 str << '\'' << passwordMaskCharacter << '\'';
708 } else {
709 str << "U+" << qSetFieldWidth(width: 4) << qSetPadChar(ch: '0') << Qt::uppercasedigits << Qt::hex
710 << passwordMaskCharacterUc << Qt::dec << qSetFieldWidth(width: 0);
711 }
712 str << '\n'
713 << " fontSmoothingGamma: " << styleHints->fontSmoothingGamma() << '\n'
714 << " useRtlExtensions: " << styleHints->useRtlExtensions() << '\n'
715 << " setFocusOnTouchRelease: " << styleHints->setFocusOnTouchRelease() << '\n'
716 << " tabFocusBehavior: " << formatQDebug(t: styleHints->tabFocusBehavior()) << '\n'
717 << " singleClickActivation: " << styleHints->singleClickActivation() << '\n';
718 str << "\nAdditional style hints (QPlatformIntegration):\n"
719 << " ReplayMousePressOutsidePopup: "
720 << platformIntegration->styleHint(hint: QPlatformIntegration::ReplayMousePressOutsidePopup).toBool() << '\n';
721
722 const QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme();
723 str << "\nTheme:"
724 "\n Platforms requested : " << platformIntegration->themeNames()
725 << "\n available : " << QPlatformThemeFactory::keys()
726#ifdef QT_WIDGETS_LIB
727 << "\n Styles requested : " << platformTheme->themeHint(hint: QPlatformTheme::StyleNames).toStringList()
728 << "\n available : " << QStyleFactory::keys()
729#endif
730 ;
731 const QString iconTheme = platformTheme->themeHint(hint: QPlatformTheme::SystemIconThemeName).toString();
732 if (!iconTheme.isEmpty()) {
733 str << "\n Icon theme : " << iconTheme
734 << ", " << platformTheme->themeHint(hint: QPlatformTheme::SystemIconFallbackThemeName).toString()
735 << " from " << platformTheme->themeHint(hint: QPlatformTheme::IconThemeSearchPaths).toStringList();
736 }
737 if (const QFont *systemFont = platformTheme->font())
738 str << "\n System font : " << *systemFont<< '\n';
739
740 if (platformTheme->usePlatformNativeDialog(type: QPlatformTheme::FileDialog))
741 str << " Native file dialog\n";
742 if (platformTheme->usePlatformNativeDialog(type: QPlatformTheme::ColorDialog))
743 str << " Native color dialog\n";
744 if (platformTheme->usePlatformNativeDialog(type: QPlatformTheme::FontDialog))
745 str << " Native font dialog\n";
746 if (platformTheme->usePlatformNativeDialog(type: QPlatformTheme::MessageDialog))
747 str << " Native message dialog\n";
748
749 str << "\nFonts:\n General font : " << QFontDatabase::systemFont(type: QFontDatabase::GeneralFont) << '\n'
750 << " Fixed font : " << QFontDatabase::systemFont(type: QFontDatabase::FixedFont) << '\n'
751 << " Title font : " << QFontDatabase::systemFont(type: QFontDatabase::TitleFont) << '\n'
752 << " Smallest font: " << QFontDatabase::systemFont(type: QFontDatabase::SmallestReadableFont) << '\n';
753 if (flags & QtDiagFonts) {
754 const QStringList families = QFontDatabase::families();
755 str << "\n Families (" << families.size() << "):\n";
756 for (int i = 0, count = families.size(); i < count; ++i)
757 str << " " << families.at(i) << '\n';
758
759 const QList<int> standardSizes = QFontDatabase::standardSizes();
760 str << "\n Standard Sizes:";
761 for (int i = 0, count = standardSizes.size(); i < count; ++i)
762 str << ' ' << standardSizes.at(i);
763 QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase::writingSystems();
764 str << "\n\n Writing systems:\n";
765 for (int i = 0, count = writingSystems.size(); i < count; ++i)
766 str << " " << formatValueQDebug(t: writingSystems.at(i)) << '\n';
767 }
768
769 str << "\nPalette:\n" << QGuiApplication::palette();
770
771 const QList<QScreen*> screens = QGuiApplication::screens();
772 const int screenCount = screens.size();
773 str << "\nScreens: " << screenCount << ", High DPI scaling: "
774 << (QHighDpiScaling::isActive() ? "active" : "inactive") << '\n';
775 for (int s = 0; s < screenCount; ++s) {
776 const QScreen *screen = screens.at(i: s);
777 const QPlatformScreen *platformScreen = screen->handle();
778 const QRect geometry = screen->geometry();
779 const QDpi dpi(screen->logicalDotsPerInchX(), screen->logicalDotsPerInchY());
780 const QDpi nativeDpi = platformScreen->logicalDpi();
781 const QRect nativeGeometry = platformScreen->geometry();
782 str << '#' << ' ' << s << " \"" << screen->name() << '"'
783 << " Depth: " << screen->depth()
784 << " Primary: " << (screen == QGuiApplication::primaryScreen() ? "yes" : "no")
785 << "\n Manufacturer: " << screen->manufacturer()
786 << "\n Model: " << screen->model()
787 << "\n Serial number: " << screen->serialNumber()
788 << "\n Geometry: " << geometry;
789 if (geometry != nativeGeometry)
790 str << " (native: " << nativeGeometry << ')';
791 str << " Available: " << screen->availableGeometry();
792 if (geometry != screen->virtualGeometry())
793 str << "\n Virtual geometry: " << screen->virtualGeometry() << " Available: " << screen->availableVirtualGeometry();
794 if (screen->virtualSiblings().size() > 1)
795 str << "\n " << screen->virtualSiblings().size() << " virtual siblings";
796 str << "\n Physical size: " << screen->physicalSize() << " mm"
797 << " Refresh: " << screen->refreshRate() << " Hz"
798 << " Power state: " << platformScreen->powerState();
799 str << "\n Physical DPI: " << screen->physicalDotsPerInchX()
800 << ',' << screen->physicalDotsPerInchY()
801 << " Logical DPI: " << dpi;
802 if (dpi != nativeDpi)
803 str << " (native: " << nativeDpi << ')';
804 str << ' ' << platformScreen->subpixelAntialiasingTypeHint() << "\n ";
805 if (QHighDpiScaling::isActive())
806 str << "High DPI scaling factor: " << QHighDpiScaling::factor(context: screen) << ' ';
807 str << "DevicePixelRatio: " << screen->devicePixelRatio();
808 str << "\n Primary orientation: " << screen->primaryOrientation()
809 << " Orientation: " << screen->orientation()
810 << " Native orientation: " << screen->nativeOrientation()
811 << "\n\n";
812 }
813
814 const auto inputDevices = QInputDevice::devices();
815 if (!inputDevices.isEmpty()) {
816 str << "Input devices: " << inputDevices.size() << '\n';
817 for (auto device : inputDevices) {
818 str << " " << formatValueQDebug(t: device->type())
819 << " \"" << device->name() << "\",";
820 if (!device->seatName().isEmpty())
821 str << " seat: \"" << device->seatName() << '"';
822 str << " capabilities:";
823 const auto capabilities = device->capabilities();
824 if (capabilities.testFlag(flag: QInputDevice::Capability::Position))
825 str << " Position";
826 if (capabilities.testFlag(flag: QInputDevice::Capability::Area))
827 str << " Area";
828 if (capabilities.testFlag(flag: QInputDevice::Capability::Pressure))
829 str << " Pressure";
830 if (capabilities.testFlag(flag: QInputDevice::Capability::Velocity))
831 str << " Velocity";
832 if (capabilities.testFlag(flag: QInputDevice::Capability::NormalizedPosition))
833 str << " NormalizedPosition";
834 if (capabilities.testFlag(flag: QInputDevice::Capability::MouseEmulation))
835 str << " MouseEmulation";
836 if (capabilities.testFlag(flag: QInputDevice::Capability::Scroll))
837 str << " Scroll";
838 if (capabilities.testFlag(flag: QInputDevice::Capability::Hover))
839 str << " Hover";
840 if (capabilities.testFlag(flag: QInputDevice::Capability::Rotation))
841 str << " Rotation";
842 if (capabilities.testFlag(flag: QInputDevice::Capability::XTilt))
843 str << " XTilt";
844 if (capabilities.testFlag(flag: QInputDevice::Capability::YTilt))
845 str << " YTilt";
846 if (capabilities.testFlag(flag: QInputDevice::Capability::TangentialPressure))
847 str << " TangentialPressure";
848 if (capabilities.testFlag(flag: QInputDevice::Capability::ZPosition))
849 str << " ZPosition";
850 if (!device->availableVirtualGeometry().isNull()) {
851 const auto r = device->availableVirtualGeometry();
852 str << " availableVirtualGeometry: " << r.width() << 'x' << r.height()
853 << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
854 }
855 str << '\n';
856 }
857 str << "\n\n";
858 }
859
860#ifndef QT_NO_OPENGL
861 if (flags & QtDiagGl) {
862 dumpGlInfo(str, listExtensions: flags & QtDiagGlExtensions);
863 str << "\n";
864 }
865#else
866 Q_UNUSED(flags);
867#endif // !QT_NO_OPENGL
868
869#if QT_CONFIG(vulkan)
870 if (flags & QtDiagVk) {
871 dumpVkInfo(str);
872 str << "\n\n";
873 }
874#endif // vulkan
875
876#ifdef Q_OS_WIN
877 // On Windows, this will provide addition GPU info similar to the output of dxdiag.
878 using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
879 if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration())) {
880 const QVariant gpuInfoV = nativeWindowsApp->gpuList();
881 if (gpuInfoV.typeId() == QMetaType::QVariantList) {
882 const auto gpuList = gpuInfoV.toList();
883 for (int i = 0; i < gpuList.size(); ++i) {
884 const QString description =
885 gpuList.at(i).toMap().value(QStringLiteral("printable")).toString();
886 if (!description.isEmpty())
887 str << "\nGPU #" << (i + 1) << ":\n" << description << '\n';
888 }
889 str << "\n";
890 }
891 }
892#endif // Q_OS_WIN
893
894 if (flags & QtDiagRhi) {
895 dumpRhiInfo(str);
896 str << "\n";
897 }
898
899 return result;
900}
901
902QT_END_NAMESPACE
903

source code of qttools/src/qtdiag/qtdiag.cpp